The SQL EXISTS
predicate is used to specify a test for a non-empty set. It returns TRUE
or FALSE
, depending on the outcome of the test.
When we incorporate the EXISTS
predicate operator into our SQL queries, we specify a subquery to test for the existence of rows. If there are any rows, then the subquery is TRUE
. If there are no rows, then the subquery is FALSE
.
Example
Here’s a quick example:
SELECT ArtistName
FROM Artists ar
WHERE EXISTS (
SELECT * FROM Albums al
WHERE al.ArtistId = ar.ArtistId
AND ReleaseDate < '2000-01-01'
);
Result:
+-------------+ | ArtistName | +-------------+ | Iron Maiden | | AC/DC | | Jim Reeves | | Buddy Rich | | Tom Jones | +-------------+
Here, my query returns the names of all artists that released at least one album before the year 2000.
The subquery returns all albums from each artist that have a ReleaseDate
prior to 2000-01-01
. The outer query uses the SQL EXISTS
operator to test for the existence of any rows that are returned by the subquery. Basically, we only get artists that have at least one row returned by the subquery. In this case, EXISTS
returns TRUE
and we get a row for that artist.
The above query is the equivalent of the following:
SELECT ArtistName
FROM Artists ar
WHERE ar.ArtistId IN (
SELECT ar.ArtistId FROM Albums al
WHERE al.ArtistId = ar.ArtistId
AND ReleaseDate < '2000-01-01'
);
Result:
+-------------+ | ArtistName | +-------------+ | Iron Maiden | | AC/DC | | Jim Reeves | | Buddy Rich | | Tom Jones | +-------------+ 5 rows in set (0.00 sec)
The difference is that the second query uses the SQL IN
predicate instead of EXISTS
. When we use IN
, we specify a column name that should be returned by the subquery. When we use EXISTS
, we don’t need to specify any such column.
A NOT EXISTS
Example
We can use NOT EXISTS
in order to return the opposite of EXISTS
:
SELECT ArtistName
FROM Artists ar
WHERE NOT EXISTS (
SELECT * FROM Albums al
WHERE al.ArtistId = ar.ArtistId
AND ReleaseDate < '2000-01-01'
);
Result:
+------------------------+ | ArtistName | +------------------------+ | Allan Holdsworth | | Devin Townsend | | Maroon 5 | | The Script | | Lit | | Black Sabbath | | Michael Learns to Rock | | Carabao | | Karnivool | | Birds of Tokyo | | Bodyjar | +------------------------+ 11 rows in set (0.00 sec)
Note that this is not the same as changing the less than sign to a greater than sign in our subquery. Here’s what happens when we do that:
SELECT ArtistName
FROM Artists ar
WHERE EXISTS (
SELECT * FROM Albums al
WHERE al.ArtistId = ar.ArtistId
AND ReleaseDate > '2000-01-01'
);
Result:
+------------------------+ | ArtistName | +------------------------+ | Devin Townsend | | The Script | | Michael Learns to Rock | | Tom Jones | | Allan Holdsworth | +------------------------+ 5 rows in set (0.00 sec)
Only five rows are returned, compared to the 11 rows returned when using NOT EXISTS
.
The logic between these two examples is different:
- The first example (using
NOT EXISTS
) returns all artists that don’t have an album released prior to the year 2000. This could include artists that haven’t released any albums at any time. - The second example requires that the artist has released at least one album after the given date. This therefore excludes any artists who haven’t released any albums after that date, as well as artists that haven’t released any albums at all.
Oh in case you’re wondering, my database is just a sample database, and it in no way represents a complete list of the actual albums released by those artists.
The Purpose of the Subquery
When using the EXISTS
operator, the purpose of the subquery is merely to return at least one row, or none at all. The contents of the subquery is not normally important. And the number of rows returned by the subquery is not important. What is important is whether at least one row is returned or not – regardless of its contents.
Because of the above, it’s unimportant which columns are included in the SELECT
list. Therefore, in the above examples we could replace SELECT *
with SELECT al.ArtistId
, or even SELECT 1
if we wanted. The result would be the same:
SELECT ArtistName
FROM Artists ar
WHERE EXISTS (
SELECT 1 FROM Albums al
WHERE al.ArtistId = ar.ArtistId
AND ReleaseDate < '2000-01-01'
);
Result:
+-------------+ | ArtistName | +-------------+ | Iron Maiden | | AC/DC | | Jim Reeves | | Buddy Rich | | Tom Jones | +-------------+
Also, it pays to bear in mind that the subquery may only be executed long enough to determine whether at least one row is returned, and not all the way to completion. In other words, the subquery may not necessarily return all rows that satisfy its criteria. Therefore, you may want to avoid using a subquery that relies on all rows being returned (such as a subquery that calls sequence functions, etc).
SQL IF EXISTS
Depending on your RDBMSs you may be able to use the following IF EXISTS
syntax to check whether an object exists before performing some action against it.
Here’s an example:
IF EXISTS
(SELECT object_id FROM sys.tables
WHERE name = 'Artists'
AND SCHEMA_NAME(schema_id) = 'dbo')
PRINT 'The table exists'
ELSE
PRINT 'The table does not exist';
Result:
The table exists
I ran this in SQL Server.
The query could be modified to perform some other, more meaningful action. For example, it could drop a table only if it exists:
IF EXISTS (
SELECT * FROM information_schema.tables
WHERE table_schema = 'dbo' AND table_name = 't1')
DROP TABLE dbo.t1;
We might want to do this in order to avoid any errors that might occur from trying to drop a table that doesn’t actually exist.
However, many RDBMSs support the DROP TABLE IF EXISTS
syntax, which is a more concise way to do it.
SQL IF NOT EXISTS
Similar to the SQL IF EXISTS
statement, we can negate it by using NOT
. This can be handy if we want to create an object instead of drop it. For example, we could use IF NOT EXISTS
to create a table only if it doesn’t already exist. This will help us avoid any errors that would occur from trying to create a table with the same name of an existing one.
Example:
IF NOT EXISTS (
SELECT * FROM sys.tables t
JOIN sys.schemas s ON (t.schema_id = s.schema_id)
WHERE s.name = 'dbo' AND t.name = 't1')
CREATE TABLE dbo.t1 (
c1 int,
c2 varchar(10)
);
Here, the subquery checks the sys.tables
view for the existence of a table of the given name (t1
) under the schema of the given name (dbo
). We could alternatively check the information_schema.tables
view, or other views/tables that the RDBMS uses for its metadata.
SQL CREATE ... IF NOT EXISTS
Many RDBMSs support the IF NOT EXISTS
syntax when creating objects, such as tables. This saves us from having to write out more complex code like in the previous example. Here’s how we can replace the previous example using this syntax:
CREATE TABLE IF NOT EXISTS t1 (
c1 int,
c2 varchar(10)
);
This works in many RDBMSs, but not in SQL Server or Oracle pre-23c at the time of writing (Oracle introduced the IF NOT EXISTS
syntax as a new feature in Oracle Database 23c).
SQL DROP ... IF EXISTS
Similarly, we can use IF EXISTS
when dropping an object from the database:
DROP TABLE IF EXISTS t1;
Again, this provides us with a more concise way of checking for the existence of the object before dropping it.