- Categories:
JOIN¶
A JOIN operation combines rows from two tables — or other table-like sources, such as
views or table functions — to create a new combined row that can be used in the query.
For a conceptual explanation of joins, see Working with joins.
This topic describes how to use the JOIN subclause in the FROM clause.
The JOIN subclause specifies, explicitly or implicitly, how to relate rows
in one table to the corresponding rows in the other table. You can also use the ASOF JOIN
subclause, which is used to join time-series data on timestamp columns when their values closely follow each other,
precede each other, or match exactly.
Although the recommended way to join tables is to use JOIN with the ON subclause of the FROM clause,
an alternative way to join tables is to use the WHERE clause. For details, see the documentation for the
WHERE clause.
Syntax¶
Use one of the following:
Parameters¶
object_ref1andobject_ref2Each object reference is a table or table-like data source.
JOINUse the
JOINkeyword to specify that the tables should be joined. CombineJOINwith other join-related keywords — for example,INNERorOUTER— to specify the type of join.The semantics of joins are as follows (for brevity, this topic uses
o1ando2forobject_ref1andobject_ref2, respectively).Join Type Semantics o1 INNER JOIN o2For each row of o1, a row is produced for each row ofo2that matches according to theON conditionsubclause. (You can also use a comma to specify an inner join. For an example, see the examples section.) If you useINNER JOINwithout theONclause, or if you use a comma without aWHEREclause, the result is the same as usingCROSS JOIN: a Cartesian product; every row ofo1paired with every row ofo2.o1 LEFT OUTER JOIN o2The result of the inner join is augmented with a row for each row of o1that has no matches ino2. The result columns referencingo2contain null.o1 RIGHT OUTER JOIN o2The result of the inner join is augmented with a row for each row of o2that has no matches ino1. The result columns referencingo1contain null.o1 FULL OUTER JOIN o2Returns all joined rows, plus one row for each unmatched left side row (extended with nulls on the right), plus one row for each unmatched right side row (extended with nulls on the left). o1 CROSS JOIN o2For every possible combination of rows from o1ando2(that is, Cartesian product), the joined table contains a row consisting of all columns ino1followed by all columns ino2. ACROSS JOINcan’t be combined with anON conditionclause. However, you can use aWHEREclause to filter the results.o1 NATURAL JOIN o2A NATURAL JOINis identical to an explicitJOINon the common columns of the two tables, except that the common columns are included only once in the output. (A natural join assumes that columns with the same name, but in different tables, contain corresponding data.) For examples, see the examples section. ANATURAL JOINcan be combined with anOUTER JOIN. ANATURAL JOINcan’t be combined with anON conditionclause because theJOINcondition is already implied. However, you can use aWHEREclause to filter the results.The
DIRECTEDkeyword specifies a directed join, which enforces the join order of the tables. The first, or left, table is scanned before the second, or right, table. For example,o1 INNER DIRECTED JOIN o2scans theo1table before theo2table. Directed joins are useful in the following situations:- You are migrating workloads into Snowflake that have join order directives.
- You want to improve performance by scanning join tables in a specific order.
To choose which table is scanned first without rewriting the order in which you write the tables, add a read-order clause after the
ONorUSINGclause. For details, see the read-order clause.Default:
INNER JOINIf the word
JOINis used without specifyingINNERorOUTER, then theJOINis an inner join.If the
DIRECTEDkeyword is added, the join type (for example,INNER,LEFT,RIGHT, orFULL) is required.See also:
ON conditionA Boolean expression that defines the rows from the two sides of the
JOINthat are considered to match, for example:Conditions are discussed in more detail in the WHERE clause documentation.
The
ONclause is prohibited forCROSS JOIN.The
ONclause is unnecessary, and prohibited, forNATURAL JOINbecause the join columns are implied.For other joins, the
ONclause is optional. However, omitting theONclause results in a Cartesian product; every row ofobject_ref1paired with every row ofobject_ref2. A Cartesian product can produce a very large volume of output, almost all of which consists of pairs of rows that aren’t actually related, which consumes a lot of resources and is often a user error.USING( column_list )A list of columns in common between the two tables being joined. These columns are used as the join columns. The columns must have the same name and meaning in each of the tables being joined.
For example, suppose that the SQL statement contains:
In the simple case, this would be equivalent to:
In the standard JOIN syntax, the projection list (the list of columns and other expressions after the SELECT keyword) is
*. This causes the query to return thekey_columnexactly once. The columns are returned in the following order:- The columns in the
USINGclause in the order specified. - The left table columns not specified in the
USINGclause. - The right table columns not specified in the
USINGclause.
For examples of standard and nonstandard usage, see the examples section.
- The columns in the
{ FIRST | LAST } ( alias )Specifies which side of a directed join is scanned first. This clause is called the read-order clause.
FIRST( alias )names the table that is scanned first, andLAST( alias )names the table that is scanned last.A join has two sides, so the two forms are interchangeable. For
o1 INNER DIRECTED JOIN o2, specifyingFIRST(o2)is equivalent to specifyingLAST(o1). Use whichever form is clearer. You can specify only one read-order clause for a single join.The
aliasmust name one of the two tables that the join combines. If the table doesn’t have an explicit alias, use the table name.This clause is useful when you want to set the scan order of one join inside a chain of joins. Without it, you must restructure the query: move a table to the front of the
FROMclause, parenthesize the remaining join, and move the join condition to change which side is scanned first. For an example, see Set the scan order for a directed join.For restrictions, see the usage notes.
Default: The left table is scanned first.
Usage notes¶
-
The following restrictions apply to table functions other than SQL UDTFs:
-
You can’t specify the
ON,USING, orNATURAL JOINclause in a lateral table function, other than a SQL UDTF.For example, the following syntax is not allowed:
-
You can’t specify the
ON,USING, orNATURAL JOINclause in an outer lateral join to a table function, other than a SQL UDTF.For example, the following syntax is not allowed:
Using this syntax results in the following error:
-
These restrictions don’t apply if you are using a comma, rather than a JOIN keyword:
-
-
The following restrictions apply to the read-order clause (
{ FIRST | LAST } ( alias )):-
The join must specify the
DIRECTEDkeyword and anONorUSINGclause. UsingFIRSTorLASTon a join that isn’t directed returns the following error: -
The clause isn’t supported for
CROSS DIRECTED JOIN,NATURAL ... DIRECTED JOIN, or a directed join that omits theONandUSINGclauses. In those positions,FIRSTorLASTis parsed as an alias for the second table rather than as a read-order clause. If that table has no alias of its own, the statement succeeds and the requested scan order is silently ignored; if it already has one, the statement fails with a syntax error. Add anONorUSINGclause to set the scan order. -
The
aliasmust name one of the two tables that the join combines. When one side of the join is itself a join (for example, the accumulated result of earlier joins in a chain), that side can’t be named, so name the table on the other side instead. Naming anything else returns the following error: -
You can specify at most one read-order clause for a single join, but you can specify a read-order clause on more than one join in the same query.
-
FIRSTandLASTaren’t reserved keywords. You can continue to use them as table names, column names, and aliases.
-
Examples¶
Many of the JOIN examples use two tables: t1 and t2. Create these tables and insert data:
The following examples run queries with joins:
- Run a query with an inner join
- Run a query with a left outer join
- Run a query with a right outer join
- Run a query with a full outer join
- Run a query with a cross join
- Run a query with a natural join
- Run a query that combines joins in the FROM clause
- Run queries with joins that use the USING clause
- Set the scan order for a directed join
Run a query with an inner join¶
The following example runs a query with an inner join:
Run the same query with an inner-directed join to enforce the join order so that the left table is scanned first:
Run a query with a left outer join¶
The following example runs a query with a left outer join:
In the output, there is a NULL value for the row in table t1 that doesn’t have a matching row
in table t2:
Run a query with a right outer join¶
The following example runs a query with a right outer join:
In the output, there is a NULL value for the row in table t1 that doesn’t have a matching
row in table t2.
Run a query with a full outer join¶
The following example runs a query with a full outer join:
Each table has a row that doesn’t have a matching row in the other table, so the output contains two rows with NULL values:
Run a query with a cross join¶
The following example runs a query with a cross join:
Note
A cross join doesn’t have an ON clause.
The output shows that the query produces a Cartesian product:
A cross join can be filtered by a WHERE clause, as shown in the following example:
Run a query with a natural join¶
The following example shows a query with a natural join. First, create two tables and insert data:
Run a query with a natural join:
The output shows that a natural join produces the same output as the corresponding inner join, except that the output doesn’t include a second copy of the join column:
The following example shows that you can combine natural joins with outer joins:
Run a query that combines joins in the FROM clause¶
You can combine in the FROM clause. Create a third table:
Run a query that chains together two joins in the FROM clause:
In such a query, the results are determined based on the joins taking place from left to right, although the optimizer might reorder the joins if a different join order produces the same result. If the right outer join is meant to take place before the left outer join, then write the query in the following way:
Run queries with joins that use the USING clause¶
The next two examples show standard (ISO 9075) and nonstandard usage of
the USING clause. Both are supported by Snowflake.
This first example shows standard usage. Specifically, the projection list
contains exactly *:
Even though the example query joins two tables, and each table has one column, and the query asks for all columns, the output contains one column, not two:
The following example shows nonstandard usage. The projection list contains
something other than *:
The output contains two columns, and the second column contains either a value from the second table or NULL:
Set the scan order for a directed join¶
By default, a directed join scans the left table first. The following example uses FIRST to scan the
right table (t2) first instead. The results are the same as for the corresponding inner join; only the
scan order changes:
Because a join has two sides, LAST(t1) is equivalent to FIRST(t2) in this query.
The read-order clause is most useful when you want to set the scan order of one join in a chain of joins.
Chained joins are combined from left to right, so in the following query the second join combines the
result of t1 INNER JOIN t2 with t3. Adding FIRST(t3) scans t3 first in that join, without changing
anything else about the query:
You can’t use LAST to express the same intent in this query, because the other side of that join is the
result of t1 INNER JOIN t2, which doesn’t have an alias that you can name.
To scan t3 first without the read-order clause, you must restructure the query: move t3 to the front of
the FROM clause, parenthesize the other join, and move the join condition to the outer join. The following
query is equivalent to the preceding one: