A newer version of this documentation is available.

View Latest

Comma-Separated Join

    • Couchbase Server 7.1
      +
      A comma-separated join enables you to produce new input objects by creating a Cartesian product of all the source objects.

      Purpose

      A comma-separated join is used within the FROM clause. Like the JOIN clause, it creates an input object by combining two or more source objects. A comma-separated join can combine arbitrary fields from the source documents, and you can chain several comma-separated joins together.

      The comma-separated join, by itself, does not specify a join predicate. This means that, in its basic form, the comma-separated join would produce all the possible combinations of the combined source objects — this is known as the Cartesian product.

      In practice, it is common to use the query’s WHERE clause to specify a condition for the comma-separated join. Refer to the examples below for further details.

      Prerequisites

      For you to select data from keyspace or expression, you must have the query_select privilege on that keyspace. For more details about user roles, see Authorization.

      Syntax

      comma-separated-join ::= ',' ( rhs-keyspace | rhs-subquery | rhs-generic )
      Syntax diagram

      Left-Hand Side

      The comma-separated join cannot be the first term within the FROM clause; it must be preceded by another FROM term. The term immediately preceding the comma-separated join represents the left-hand side of the comma-separated join.

      You can chain the comma-separated join with any of the other permitted FROM terms, including another comma-separated join. For more information, see the page on the FROM clause.

      There are restrictions on what types of FROM terms may be chained and in what order — see the descriptions on this page for more details.

      The types of FROM term that may be used as the left-hand side of the comma-separated join are summarized in the following table.

      Type Example

      keyspace identifier

      `travel-sample`.inventory.hotel

      generic expression

      20+10 AS Total

      subquery

      SELECT ARRAY_AGG(t1.city) AS cities,
        SUM(t1.city_cnt) AS apnum
      FROM (
        SELECT city, city_cnt, country,
          ARRAY_AGG(airportname) AS apnames
        FROM `travel-sample`.inventory.airport
        GROUP BY city, country
        LETTING city_cnt = COUNT(city)
      ) AS t1
      WHERE t1.city_cnt > 5;

      previous join, nest, or unnest

      SELECT *
      FROM `travel-sample`.inventory.route AS rte
      JOIN `travel-sample`.inventory.airport AS apt
        ON rte.destinationairport = apt.faa
      NEST `travel-sample`.inventory.landmark AS lmk
        ON apt.city = lmk.city
      LIMIT 5;

      previous comma-separated join

      SELECT a.airportname, h.name AS hotel, l.name AS landmark
      FROM `travel-sample`.inventory.airport AS a,
           `travel-sample`.inventory.hotel AS h,
           `travel-sample`.inventory.landmark AS l
      WHERE a.city = h.city
        AND h.city = l.city
      LIMIT 5;

      The comma-separated join is a type of inner join. For each joined object produced, both the left-hand side and right-hand side source objects must be non-MISSING and non-NULL.

      The right-hand side of a comma-separated join may be a keyspace reference, a subquery, or a generic expression term.

      Right-Hand Side Keyspace

      rhs-keyspace ::= keyspace-ref ( 'AS'? alias )? ansi-join-hints?
      Syntax diagram
      keyspace-ref

      Keyspace Reference

      alias

      AS Alias

      ansi-join-hints

      USE Clause

      Keyspace Reference

      Keyspace reference for the right-hand side of the comma-separated join. For details, see Keyspace Reference.

      AS Alias

      Assigns another name to the keyspace reference. For details, see AS Clause.

      Assigning an alias to the keyspace reference is optional. If you assign an alias to the keyspace reference, the AS keyword may be omitted.

      USE Clause

      Enables you to specify that the join should use particular keys, a particular index, or a particular join method. For details, see ANSI JOIN Hints.

      You can also supply a join hint within a specially-formatted hint comment. Note that you cannot specify a join hint for the same keyspace using both the USE clause and a hint comment. If you do this, the USE clause and the hint comment are both marked as erroneous and ignored by the optimizer.

      Right-Hand Side Subquery

      rhs-subquery ::= subquery-expr 'AS'? alias
      Syntax diagram
      subquery-expr

      Subquery Expression

      alias

      AS Alias

      Subquery Expression

      Use parentheses to specify a subquery for the right-hand side of the comma-separated join. For details, see Subquery Expression.

      A subquery on the right-hand side of the comma-separated join cannot be correlated, i.e. it cannot refer to a keyspace in the outer query block. This will lead to an error.

      AS Alias

      Assigns another name to the subquery. For details, see AS Clause.

      You must assign an alias to a subquery on the right-hand side of the join. However, when you assign an alias to the subquery, the AS keyword may be omitted.

      Right-Hand Side Generic Expression

      rhs-generic ::= expr ( 'AS'? alias )?
      Syntax diagram

      Expression Term

      A N1QL expression generating JSON documents or objects for the right-hand side of the comma-separated join.

      An expression on the right-hand side of the comma-separated join may be correlated, i.e. it may refer to a keyspace on the left-hand side of the join. In this case, only a nested-loop join may be used.

      AS Alias

      Assigns another name to the generic expression. For details, see AS Clause.

      You must assign an alias to the generic expression if it is not an identifier; otherwise, assigning an alias is optional. However, when you assign an alias to the generic expression, the AS keyword may be omitted.

      Limitations

      • You can chain comma-separated joins with ANSI JOIN clauses, ANSI NEST clauses, and UNNEST clauses. However, you cannot chain comma-separated joins with lookup JOIN and NEST clauses, or index JOIN and NEST clauses.

      • The right-hand side of a comma-separated join can only be a keyspace identifier, a subquery, or a generic expression. This means that comma-separated joins must come after any JOIN, NEST, or UNNEST clauses.

      Examples

      Example 1. Cartesian product

      The following query lists every possible combination of the two input objects.

      Comma-separated join
      SELECT * FROM [{"abc": 1}, {"abc": 2}, {"abc": 3}] AS a,
                    [{"xyz": 1}, {"xyz": 2}] AS b;

      Compare the query above with the following query using an ANSI join.

      ANSI join
      SELECT * FROM [{"abc": 1}, {"abc": 2}, {"abc": 3}] AS a
               JOIN [{"xyz": 1}, {"xyz": 2}] AS b ON true;

      The results of the two queries are the same.

      Results
      [
        {
          "a": {
            "abc": 1
          },
          "b": {
            "xyz": 1
          }
        },
        {
          "a": {
            "abc": 1
          },
          "b": {
            "xyz": 2
          }
        },
        {
          "a": {
            "abc": 2
          },
          "b": {
            "xyz": 1
          }
        },
        {
          "a": {
            "abc": 2
          },
          "b": {
            "xyz": 2
          }
        },
        {
          "a": {
            "abc": 3
          },
          "b": {
            "xyz": 1
          }
        },
        {
          "a": {
            "abc": 3
          },
          "b": {
            "xyz": 2
          }
        }
      ]
      Example 2. Comma-separated join condition

      The following query uses the WHERE clause to define the condition for a comma-separated join.

      Comma-separated join
      SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r,
           `travel-sample`.inventory.airport AS a
      WHERE a.faa = r.sourceairport
      LIMIT 4;

      Compare the query above with the following query using an ANSI join.

      ANSI join
      SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r
      JOIN `travel-sample`.inventory.airport AS a
        ON a.faa = r.sourceairport
      LIMIT 4;

      The results of the two queries are the same.

      Results
      [
        {
          "airport": "Lehigh Valley Intl",
          "route": 20010
        },
        {
          "airport": "Lehigh Valley Intl",
          "route": 20011
        },
        {
          "airport": "Lehigh Valley Intl",
          "route": 28856
        },
        {
          "airport": "Lehigh Valley Intl",
          "route": 28857
        }
      ]
      Example 3. Comma-separated join with filters

      The following query uses the WHERE clause to define a condition for a comma-separated join and to filter the query.

      Comma-separated join
      SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r,
           `travel-sample`.inventory.airport AS a
      WHERE a.faa = r.sourceairport
        AND r.sourceairport = "SFO"
      LIMIT 4;

      Compare the query above with the following query using an ANSI join.

      ANSI join
      SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r
      JOIN `travel-sample`.inventory.airport AS a
        ON a.faa = r.sourceairport
      WHERE r.sourceairport = "SFO"
      LIMIT 4;

      The results of the two queries are the same.

      Results
      [
        {
          "airport": "San Francisco Intl",
          "route": 10624
        },
        {
          "airport": "San Francisco Intl",
          "route": 10625
        },
        {
          "airport": "San Francisco Intl",
          "route": 11212
        },
        {
          "airport": "San Francisco Intl",
          "route": 11213
        }
      ]
      Example 4. Comma-separated join with hints

      The following query uses the USE clause to specify hints for a comma-separated join.

      Comma-separated join
      EXPLAIN SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r,
           `travel-sample`.inventory.airport AS a
           USE INDEX(def_inventory_airport_faa) NL
      WHERE a.faa = r.sourceairport
        AND r.sourceairport = "SFO"
      LIMIT 4;

      Compare the query above with the following query using an ANSI join.

      ANSI join
      EXPLAIN SELECT a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.route AS r
      JOIN `travel-sample`.inventory.airport AS a
       USE INDEX(def_inventory_airport_faa) NL
        ON a.faa = r.sourceairport
      WHERE r.sourceairport = "SFO"
      LIMIT 4;

      The results of the two queries are the same.

      Results
      [
        {
          "optimizer_hints": {
            "hints_followed": [
              "USE_NL(a)",
              "INDEX(a def_inventory_airport_faa)"
            ]
          },
          // ...
        }
      ]
      Example 5. Chaining ANSI joins with comma-separated joins

      The following query chains an ANSI join with a comma-separated join.

      Query
      SELECT l.name AS airline, a.airportname AS airport, r.id AS route
      FROM `travel-sample`.inventory.airline AS l
      JOIN `travel-sample`.inventory.route AS r
        ON META(l).id = r.airlineid,
           `travel-sample`.inventory.airport AS a
      WHERE a.faa = r.sourceairport
        AND r.sourceairport = "SFO"
      LIMIT 4;
      Results
      [
        {
          "airline": "AirTran Airways",
          "airport": "San Francisco Intl",
          "route": 25480
        },
        {
          "airline": "AirTran Airways",
          "airport": "San Francisco Intl",
          "route": 25481
        },
        {
          "airline": "AirTran Airways",
          "airport": "San Francisco Intl",
          "route": 25482
        },
        {
          "airline": "AirTran Airways",
          "airport": "San Francisco Intl",
          "route": 25483
        }
      ]