Set operators

Set operators combine the intermediate results of multiple query blocks into a single result set.

General syntax

[ ( ] <query> [ ) ] { INTERSECT | { MINUS | EXCEPT } | UNION [ ALL ] } [ ( ] <query> [ ) ]
[ ORDER BY ... ]
[ LIMIT ... ]
Copy

General usage notes

  • Each query can itself contain query operators to allow any number of query expressions to be combined with set operators.

  • The ORDER BY and LIMIT / FETCH clauses are applied to the result of the set operator.

  • When using these operators:

    • Make sure that each query selects the same number of columns.

    • Make sure that the data type of each column is consistent across the rows from different sources. One of the examples in the Use the UNION operator and cast mismatched data types section illustrates the potential problem and solution when data types don’t match.

    • In general, the “meanings”, as well as the data types, of the columns should match. The following won’t produce the desired results:

      SELECT LastName, FirstName FROM employees
      UNION ALL
      SELECT FirstName, LastName FROM contractors;
      
      Copy

      The risk of error increases when using the asterisk to specify all columns of a table, for example:

      SELECT * FROM table1
      UNION ALL
      SELECT * FROM table2;
      
      Copy

      If the tables have the same number of columns, but the columns aren’t in the same order, the query results will probably be incorrect.

    • The names of the output columns are based on the names of the columns of the first query. For example, consider the following query:

      SELECT LastName, FirstName FROM employees
      UNION ALL
      SELECT FirstName, LastName FROM contractors;
      
      Copy

      This query behaves as though the query were the following:

      SELECT LastName, FirstName FROM employees
      UNION ALL
      SELECT FirstName AS LastName, LastName AS FirstName FROM contractors;
      
      Copy
  • The precedence of the set operators matches the ANSI and ISO SQL standards:

    • The UNION [ALL] and MINUS (EXCEPT) operators have equal precedence.

    • The INTERSECT operator has higher precedence than UNION [ALL] and MINUS (EXCEPT).

    Operators of equal precedence are processed from left to right.

    You can use parentheses to force the expressions to be evaluated in a different order.

    Not all database vendors follow the ANSI/ISO standard for precedence of set operators. Snowflake recommends using parentheses to specify the order of evaluation, especially if you are porting code from another vendor to Snowflake, or writing code that you might execute on other databases as well as on Snowflake.

Sample tables for examples

The examples in this topic use the following sample tables. Both tables have a zip code column. One table records the zip code of each sales office, while the other records the zip code of each customer.

CREATE OR REPLACE TABLE sales_office_zip_example(
  office_name VARCHAR,
  zip VARCHAR);

INSERT INTO sales_office_zip_example VALUES ('sales1', '94061');
INSERT INTO sales_office_zip_example VALUES ('sales2', '94070');
INSERT INTO sales_office_zip_example VALUES ('sales3', '98116');
INSERT INTO sales_office_zip_example VALUES ('sales4', '98005');

CREATE OR REPLACE TABLE customer_zip_example(
  customer VARCHAR,
  zip VARCHAR);

INSERT INTO customer_zip_example VALUES ('customer1', '94066');
INSERT INTO customer_zip_example VALUES ('customer2', '94061');
INSERT INTO customer_zip_example VALUES ('customer3', '98444');
INSERT INTO customer_zip_example VALUES ('customer4', '98005');
Copy

INTERSECT

Returns rows from one query’s result set which also appear in another query’s result set, with duplicate elimination.

Syntax

SELECT ...
INTERSECT
SELECT ...
Copy

INTERSECT operator examples

Query the sample tables to find the zip codes that have both a sales office and a customer:

SELECT zip FROM sales_office_zip_example
INTERSECT
SELECT zip FROM customer_zip_example;
Copy
+-------+
| ZIP   |
|-------|
| 94061 |
| 98005 |
+-------+

MINUS , EXCEPT

Returns the rows returned by the first query that aren’t also returned by the second query.

The MINUS and EXCEPT keywords have the same meaning and can be used interchangeably.

Syntax

SELECT ...
MINUS
SELECT ...

SELECT ...
EXCEPT
SELECT ...
Copy

MINUS operator examples

Query the sample tables to find the zip codes in the sales_office_zip_example table that aren’t also in the customer_zip_example table:

SELECT zip FROM sales_office_zip_example
MINUS
SELECT zip FROM customer_zip_example;
Copy
+-------+
| ZIP   |
|-------|
| 98116 |
| 94070 |
+-------+

Query the sample tables to find the zip codes in the customer_zip_example table that aren’t also in the sales_office_zip_example table:

SELECT zip FROM customer_zip_example
MINUS
SELECT zip FROM sales_office_zip_example;
Copy
+-------+
| ZIP   |
|-------|
| 98444 |
| 94066 |
+-------+

UNION [ ALL ]

Combines the result sets from two queries:

  • UNION combines with duplicate elimination.

  • UNION ALL combines without duplicate elimination.

The default is UNION (that is, with duplicate elimination).

Syntax

SELECT ...
UNION [ ALL ]
SELECT ...
Copy

UNION operator examples

The following examples use the UNION operator.

Use the UNION operator to combine the results from two queries

Use the UNION operator to combine the result sets from two queries on the sample tables:

SELECT office_name office_or_customer, zip FROM sales_office_zip_example
UNION
SELECT customer, zip FROM customer_zip_example
ORDER BY zip;
Copy
+--------------------+-------+
| OFFICE_OR_CUSTOMER | ZIP   |
|--------------------+-------|
| sales1             | 94061 |
| customer2          | 94061 |
| customer1          | 94066 |
| sales2             | 94070 |
| sales4             | 98005 |
| customer4          | 98005 |
| sales3             | 98116 |
| customer3          | 98444 |
+--------------------+-------+

Use the UNION operator and cast mismatched data types

This example demonstrates a potential issue with using the UNION operator when data types don’t match, then provides the solution.

Start by creating the tables and inserting some data:

CREATE OR REPLACE TABLE union_test1 (v VARCHAR);
CREATE OR REPLACE TABLE union_test2 (i INTEGER);

INSERT INTO union_test1 (v) VALUES ('Adams, Douglas');
INSERT INTO union_test2 (i) VALUES (42);
Copy

Execute a UNION operation with different data types (a VARCHAR value in union_test1 and an INTEGER value in union_test2):

SELECT v FROM union_test1
UNION
SELECT i FROM union_test2;
Copy

This query returns an error:

100038 (22018): Numeric value 'Adams, Douglas' is not recognized

Now use explicit casting to convert the inputs to a compatible type:

SELECT v::VARCHAR FROM union_test1
UNION
SELECT i::VARCHAR FROM union_test2;
Copy
+----------------+
| V::VARCHAR     |
|----------------|
| Adams, Douglas |
| 42             |
+----------------+