Merging Query Results in SQL

9
0

Introduction

A training program keeps separate lists of online and onsite attendees. Some learners appear in both lists. To prepare one attendance report, you need to decide whether those learners should appear once or whether every entry from both lists should remain.

SQL set operators combine query results under a shared column layout. UNION and UNION ALL handle the two versions of this report, while INTERSECT finds shared rows and EXCEPT finds rows present only in the first result. Choosing the operator starts with deciding what one row in your report should represent.

How SQL Combines Query Results

A result set is the collection of rows returned by a query. A set operation takes the output of two queries and produces one result set. These SELECT queries read the source data without changing either source table.

The inputs can come from different tables, filtered queries on one table, or more complex queries. What matters is the layout of the selected columns. Both inputs must return the same number of columns, and corresponding positions must have compatible data types. These requirements are called union compatibility.

The main operations answer different questions:

Operator

Rows included

Repeated selected rows

UNION

Rows appearing in either result

Kept once

UNION ALL

Every row from both results

Preserved

INTERSECT

Rows appearing in both results

Kept once

EXCEPT

Rows in the first result that are absent from the second

Kept once

Here, a repeated row means the selected values match across the whole row. Two records about the same person can still be different output rows if one of their selected values differs.

UNION and UNION ALL with Examples

Consider an online_attendees table that stores online attendees and an onsite_attendees table that stores onsite attendees. Both tables contain attendee_id and attendee_name columns.

online_attendees

attendee_id

attendee_name

101

Asha

102

Kabir

103

Meera

onsite_attendees

attendee_id

attendee_name

102

Kabir

103

Meera

104

Ravi

The attendee_id column is the primary key in each table, so the same ID cannot appear more than once within that table.

CREATE TABLE online_attendees (
attendee_id INTEGER PRIMARY KEY,
attendee_name VARCHAR(40) NOT NULL
);
CREATE TABLE onsite_attendees (
attendee_id INTEGER PRIMARY KEY,
attendee_name VARCHAR(40) NOT NULL
);
INSERT INTO online_attendees (attendee_id, attendee_name)
VALUES
(101, 'Asha'),
(102, 'Kabir'),
(103, 'Meera');
INSERT INTO onsite_attendees (attendee_id, attendee_name)
VALUES
(102, 'Kabir'),
(103, 'Meera'),
(104, 'Ravi');

Use UNION to return distinct rows

Suppose the report needs one entry for each attendee represented in these lists. Select the ID and name from each table, with UNION between the two SELECT statements.

SELECT attendee_id, attendee_name
FROM online_attendees
UNION
SELECT attendee_id, attendee_name
FROM onsite_attendees
ORDER BY attendee_id, attendee_name;

Expected result

attendee_id

attendee_name

101

Asha

102

Kabir

103

Meera

104

Ravi

The two queries return six rows in total. Kabir’s row and Meera’s row each appear twice. UNION keeps one copy of each repeated row, leaving four rows.

UNION also removes repeated selected rows within a single input result. Its duplicate removal applies to the combined output. Writing UNION DISTINCT makes the same default behavior explicit.

Use UNION ALL to preserve every row

Now suppose each appearance in a source list represents a participation record that should be retained. UNION ALL preserves all six rows.

SELECT attendee_id, attendee_name
FROM online_attendees
UNION ALL
SELECT attendee_id, attendee_name
FROM onsite_attendees
ORDER BY attendee_id, attendee_name;

Expected result

attendee_id

attendee_name

101

Asha

102

Kabir

102

Kabir

103

Meera

103

Meera

104

Ravi

The repeated entries now carry useful information: Kabir and Meera each appear in two source lists. Removing those entries would change a count of participation records. However, these two selected columns do not identify the source of each entry. A source column can provide that information, as shown later.

The diagram compares the same inputs under both operators. The repeated rows are highlighted, and both outputs are sorted using the final ORDER BY clause.

Union or Union ALL

Union or Union ALL

Compare UNION and UNION ALL

Question

UNION

UNION ALL

What happens to repeated selected rows?

One copy remains

Every copy remains

How many rows does this example return?

4

6

Does the operator remove duplicates?

Yes

No

Does it guarantee display order by itself?

No

No

When is it appropriate?

The report needs distinct selected rows

Every occurrence matters, or the inputs are known to be disjoint

UNION ALL avoids duplicate-removal work and is often faster when keeping all rows is correct. A final sort can still require work, so the choice does not guarantee a particular runtime or execution plan. Choose the correct result first, then assess performance.

INTERSECT and EXCEPT with Examples

Use INTERSECT to find common rows

To find attendees represented in both lists with the same selected ID and name, use INTERSECT.

SELECT attendee_id, attendee_name
FROM online_attendees
INTERSECT
SELECT attendee_id, attendee_name
FROM onsite_attendees
ORDER BY attendee_id, attendee_name;

Expected result

attendee_id

attendee_name

102

Kabir

103

Meera

Both complete rows occur in both inputs. Asha appears only online, while Ravi appears only onsite, so neither appears in this result.

If the real question concerns shared attendee IDs even when names disagree, select only attendee_id in both inputs. The selected columns determine what must match.

Use EXCEPT to find rows missing from the second result

To find the online rows that have no identical counterpart in the onsite result, place the online query first and use EXCEPT.

SELECT attendee_id, attendee_name
FROM online_attendees
EXCEPT
SELECT attendee_id, attendee_name
FROM onsite_attendees
ORDER BY attendee_id, attendee_name;

Expected result

attendee_id

attendee_name

101

Asha

Kabir and Meera have matching rows in the onsite result, so only Asha remains. Reversing the two inputs would return Ravi instead. The direction of EXCEPT changes the question being answered.

INTERSECT and EXCEPT remove duplicates by default. Their ALL variants account for repeated occurrences: if a particular row occurs m times on the left and n times on the right, INTERSECT ALL keeps the smaller count, while EXCEPT ALL keeps max(m - n, 0) copies. For example, counts of three and two produce two copies with INTERSECT ALL and one with EXCEPT ALL.

Rules That Keep Merged Results Correct

Match column positions and meanings

The first selected column lines up with the first column of the other input, the second with the second, and so on. Align the meanings of the selected expressions by position. Two tables may use different column names and still produce compatible results.

In this example, both queries select attendee_id followed by attendee_name. Reversing those positions in one query would mix unrelated values or cause a type error. Explicit column lists make this alignment clear and reduce surprises when a table's schema changes.

MySQL and PostgreSQL have different type-resolution rules. MySQL may convert some combinations that PostgreSQL rejects. Use matching types where practical, or deliberately cast corresponding expressions to an appropriate common type. A CAST converts an expression to a specified data type; it cannot make unrelated meanings compatible.

Compare the entire selected row

UNION checks all selected columns when deciding whether rows are duplicates. For example, the hypothetical rows (102, Kabir) and (102, Kabeer) remain distinct because the names differ. UNION does not choose which spelling is correct. Conversely, selecting only a name could collapse different people who share that name.

To retain where an entry came from, add a source column. The string literal supplies a source label for each row, and AS source names the output column.

The following query deliberately uses UNION to show how an added column changes duplicate detection.

SELECT attendee_id, attendee_name, 'online' AS source
FROM online_attendees
UNION
SELECT attendee_id, attendee_name, 'onsite' AS source
FROM onsite_attendees
ORDER BY attendee_id, source;

Expected result

attendee_id

attendee_name

source

101

Asha

online

102

Kabir

online

102

Kabir

onsite

103

Meera

online

103

Meera

onsite

104

Ravi

onsite

All six rows remain because the overlapping attendees have different source values. If the goal is to preserve every source record, UNION ALL expresses that intention directly and also preserves duplicates that might exist within a source result.

The next diagram focuses on Kabir. It shows how changing the selected columns changes which rows count as duplicates.

What Makes a Dublicate

What Makes a Dublicate

Handle NULL correctly

NULL represents missing or unknown information. In duplicate elimination, corresponding NULL values are treated as matching. That rule is different from ordinary equality: the condition NULL = NULL evaluates to unknown, rather than true.

These standalone queries use an explicit integer cast to give the output a clear type. They do not insert NULL into the attendee tables.

SELECT CAST(NULL AS SIGNED) AS attendee_id
UNION
SELECT CAST(NULL AS SIGNED);
SELECT CAST(NULL AS SIGNED) AS attendee_id
UNION ALL
SELECT CAST(NULL AS SIGNED);

The first statement returns one row containing NULL. The second returns two rows containing NULL. With multiple columns, corresponding NULL values match for duplicate elimination only when the remaining selected values also match.

Sort and limit the correct result

Use a final ORDER BY to request a display order. Without it, neither UNION nor UNION ALL guarantees an order, including an order where all rows from the first input appear first. The main examples sort by attendee_id and then attendee_name.

The output column names come from the first query. For a direct sort of the combined result, use those names or their aliases, rather than a source-table-qualified name. A final LIMIT applies to the combined output.

If the requirement is to take a subset from each input, put each input's ORDER BY and LIMIT inside its own parentheses. A separate final ORDER BY still controls the combined display.

For example, take the attendee with the highest ID from each list:

(SELECT attendee_id, attendee_name
FROM online_attendees
ORDER BY attendee_id DESC
LIMIT 1)
UNION ALL
(SELECT attendee_id, attendee_name
FROM onsite_attendees
ORDER BY attendee_id DESC
LIMIT 1)
ORDER BY attendee_id, attendee_name;

Expected result

attendee_id

attendee_name

103

Meera

104

Ravi

The online input contributes Meera, and the onsite input contributes Ravi. Moving LIMIT 1 to the end would limit the entire combined result to one row.

Apply filters and grouping deliberately

  • Filter each input where appropriate: A WHERE clause inside one SELECT affects that input. Apply a shared condition to both inputs, or wrap the set operation in an outer SELECT and filter its result.

  • Make mixed operators explicit: In MySQL and PostgreSQL, INTERSECT has higher precedence than UNION and EXCEPT. Parentheses make the intended grouping clear when several operators appear together.

  • Preserve meaningful repetitions: Two rows with equal selected values may represent separate events. Include the event identifier when it belongs in the report, and use UNION ALL when every occurrence must remain.

Summary

SQL set operators combine compatible query results into one output. UNION removes repeated selected rows, while UNION ALL preserves every occurrence. INTERSECT returns shared rows, and EXCEPT returns rows found only in the first result. Match column positions and types, compare complete selected rows, handle NULL deliberately, and use a final ORDER BY whenever the output needs a defined order.

CS Core

Read Similar Blogs

Comments0