How EXCEPT Works in PostgreSQL

In PostgreSQL, the EXCEPT operator returns rows that are returned by the left input query that aren’t returned by the right input query. This can also be referred to as the difference between two queries.

Syntax

The syntax goes like this:

query1 EXCEPT [ALL] query2

Duplicates are eliminated unless EXCEPT ALL is used.

Example

Suppose we have the following tables:

SELECT * FROM Teachers;
SELECT * FROM Students;

Result:

 teacherid | teachername 
-----------+-------------
         1 | Warren
         2 | Ben
         3 | Cathy
         4 | Cathy
         5 | Bill
         6 | Bill

 studentid | studentname 
-----------+-------------
         1 | Faye
         2 | Jet
         3 | Spike
         4 | Ein
         5 | Warren
         6 | Bill

We can use the EXCEPT operator to return teachers that aren’t also students:

SELECT TeacherName FROM Teachers
EXCEPT
SELECT StudentName FROM Students;

Result:

 teachername 
-------------
 Cathy
 Ben

So we only get values that appear in the Teachers table that don’t also appear in the Students table.

By default, the EXCEPT operator returns distinct rows. So in our example, only one row is returned for Cathy, even though there are two teachers with that name.

The above example is the equivalent of the following query:

SELECT TeacherName FROM Teachers
EXCEPT DISTINCT
SELECT StudentName FROM Students;

Result:

 teachername 
-------------
 Cathy
 Ben

This is the same result that we got when there was no explicit DISTINCT keyword. We can include duplicates with the ALL keyword (more on this later).

We can get different results, depending on which table is on the left and which is on the right. Here’s an example that puts the Students table on the left and Teachers on the right:

SELECT StudentName FROM Students
EXCEPT
SELECT TeacherName FROM Teachers;

Result:

 studentname 
-------------
 Faye
 Jet
 Spike
 Ein

This time we get students that aren’t also teachers.

Include Duplicates

By default, the EXCEPT operator implicitly applies a DISTINCT operation. In other words, it returns only distinct values by default.

We can include the ALL keyword in order to include duplicates in our result:

SELECT TeacherName FROM Teachers
EXCEPT ALL
SELECT StudentName FROM Students;

Result:

 teachername 
-------------
 Cathy
 Cathy
 Bill
 Ben

This time we got four rows, instead of the two that we got in our first example.

We can see that both Cathys were returned instead of just one like in our first example.

As for Bill? There are two Bills in the Teachers table, but only one is returned here. That’s probably because there’s one Bill in the Students table, which would exclude one of the Bills from our results.

An Alternative

It’s possible to get the same result without using the EXCEPT operator. For example, we could rewrite our first example to this:

SELECT 
    DISTINCT TeacherName
FROM Teachers t 
WHERE NOT EXISTS (SELECT StudentName FROM Students s
WHERE t.TeacherName = s.StudentName);

Result:

 teachername 
-------------
 Cathy
 Ben