6.1.8. UNION

A UNION concatenates two or more datasets, thus increasing the number of rows but not the number of columns. Datasets taking part in a UNION must have the same number of columns, and columns at corresponding positions must be of the same type. Other than that, they may be totally unrelated.

By default, a union suppresses duplicate rows. UNION ALL shows all rows, including any duplicates. The optional DISTINCT keyword makes the default behaviour explicit.

Syntax

  1. <union> ::=
  2. <individual-select>
  3. UNION [DISTINCT | ALL]
  4. <individual-select>
  5. [
  6. [UNION [DISTINCT | ALL]
  7. <individual-select>
  8. ...
  9. ]
  10. [<union-wide-clauses>]
  11. <individual-select> ::=
  12. SELECT
  13. [TRANSACTION name]
  14. [FIRST m] [SKIP n]
  15. [DISTINCT | ALL] <columns>
  16. [INTO <host-varlist>]
  17. FROM <source> [[AS] alias]
  18. [<joins>]
  19. [WHERE <condition>]
  20. [GROUP BY <grouping-list>
  21. [HAVING <aggregate-condition>]]
  22. [PLAN <plan-expr>]
  23. <union-wide-clauses> ::=
  24. [ORDER BY <ordering-list>]
  25. [ROWS m [TO n]]
  26. [FOR UPDATE [OF <columns>]]
  27. [WITH LOCK]
  28. [INTO <PSQL-varlist>]

Unions take their column names from the first select query. If you want to alias union columns, do so in the column list of the topmost SELECT. Aliases in other participating selects are allowed and may even be useful, but will not propagate to the union level.

If a union has an ORDER BY clause, the only allowed sort items are integer literals indicating 1-based column positions, optionally followed by an ASC/DESC and/or a NULLS {FIRST | LAST} directive. This also implies that you cannot order a union by anything that isn’t a column in the union. (You can, however, wrap it in a derived table, which gives you back all the usual sort options.)

Unions are allowed in subqueries of any kind and can themselves contain subqueries. They can also contain joins, and can take part in a join when wrapped in a derived table.

Examples

This query presents information from different music collections in one dataset using unions:

  1. select id, title, artist, length, 'CD' as medium
  2. from cds
  3. union
  4. select id, title, artist, length, 'LP'
  5. from records
  6. union
  7. select id, title, artist, length, 'MC'
  8. from cassettes
  9. order by 3, 2 -- artist, title;

If id, title, artist and length are the only fields in the tables involved, the query can also be written as:

  1. select c.*, 'CD' as medium
  2. from cds c
  3. union
  4. select r.*, 'LP'
  5. from records r
  6. union
  7. select c.*, 'MC'
  8. from cassettes c
  9. order by 3, 2 -- artist, title;

Qualifying the “stars” is necessary here because they are not the only item in the column list. Notice how the “c” aliases in the first and third select do not conflict with each other: their scopes are not union-wide but apply only to their respective select queries.

The next query retrieves names and phone numbers from translators and proofreaders. Translators who also work as proofreaders will show up only once in the result set, provided their phone number is the same in both tables. The same result can be obtained without DISTINCT. With ALL, these people would appear twice.

  1. select name, phone from translators
  2. union distinct
  3. select name, telephone from proofreaders;

A UNION within a subquery:

  1. select name, phone, hourly_rate from clowns
  2. where hourly_rate < all
  3. (select hourly_rate from jugglers
  4. union
  5. select hourly_rate from acrobats)
  6. order by hourly_rate;