In SQL, the HAVING
clause can be used to specify a search condition for a group or an aggregate.
The HAVING
clause is typically used with the GROUP BY
clause. In cases where it’s not, there is an implicit single, aggregated group.
The HAVING
clause is similar to the WHERE
clause, except that WHERE
filters individual rows, whereas HAVING
filters groups. The WHERE
clause filters data before it is grouped, whereas HAVING
filters data after it is grouped.
Example 1 – HAVING
with COUNT()
Suppose we have the following table:
SELECT * FROM Pets;
Result:
+---------+-------------+-----------+-----------+------------+ | PetId | PetTypeId | OwnerId | PetName | DOB | |---------+-------------+-----------+-----------+------------| | 1 | 2 | 3 | Fluffy | 2020-11-20 | | 2 | 3 | 3 | Fetch | 2019-08-16 | | 3 | 2 | 2 | Scratch | 2018-10-01 | | 4 | 3 | 3 | Wag | 2020-03-15 | | 5 | 1 | 1 | Tweet | 2020-11-28 | | 6 | 3 | 4 | Fluffy | 2020-09-17 | | 7 | 3 | 2 | Bark | NULL | | 8 | 2 | 4 | Meow | NULL | +---------+-------------+-----------+-----------+------------+
We could run the following query against this table:
SELECT
PetTypeId,
COUNT(PetTypeId) AS Count
FROM Pets
GROUP BY PetTypeId
HAVING COUNT(PetTypeId) > 2
ORDER BY Count DESC;
Result:
+-------------+---------+ | PetTypeId | Count | |-------------+---------| | 3 | 4 | | 2 | 3 | +-------------+---------+
In this case, we used the HAVING
clause in conjunction with the GROUP BY
clause to return just those rows that have a COUNT(PetTypeId)
of greater than 2
. This uses the COUNT()
function, which is a SQL standard aggregate function available in most major RDBMSs.
If we omit the HAVING
clause, we get one more result:
SELECT
PetTypeId,
COUNT(PetTypeId) AS Count
FROM Pets
GROUP BY PetTypeId
ORDER BY Count DESC;
Result:
+-------------+---------+ | PetTypeId | Count | |-------------+---------| | 3 | 4 | | 2 | 3 | | 1 | 1 | +-------------+---------+
Example 2 – HAVING
with SUM()
Here’s another example. This time we use the HAVING
clause with the SUM()
function, which is another aggregate function available in most major RDBMSs (see SQLite SUM()
for another example).
SELECT
CountryCode,
District,
SUM(Population) AS Population
FROM City
WHERE CountryCode IN ('AGO', 'ARE', 'AUS')
GROUP BY CountryCode, District
HAVING SUM(Population) > 1000000
ORDER BY CountryCode;
Result:
+---------------+-----------------+--------------+ | CountryCode | District | Population | |---------------+-----------------+--------------| | AGO | Luanda | 2022000 | | AUS | New South Wales | 3993949 | | AUS | Queensland | 1805236 | | AUS | Victoria | 2990711 | | AUS | West Australia | 1096829 | +---------------+-----------------+--------------+
Example 3 – HAVING
with the IN
Operator
You’re not limited to just the =, <
, >=
, IN
, LIKE
, etc).
Here’s an example that uses the IN
operator to specify a range of aggregate values to return.
SELECT
CountryCode,
District,
SUM(Population) AS Population
FROM City
WHERE CountryCode IN ('AGO', 'ARE', 'AUS')
GROUP BY CountryCode, District
HAVING SUM(Population) IN (2022000, 3993949, 2990711)
ORDER BY CountryCode;
Result:
+---------------+-----------------+--------------+ | CountryCode | District | Population | |---------------+-----------------+--------------| | AGO | Luanda | 2022000 | | AUS | New South Wales | 3993949 | | AUS | Victoria | 2990711 | +---------------+-----------------+--------------+
Example 4 – HAVING
without a GROUP BY
Clause
Although HAVING
is usually used with the GROUP BY
clause, it can also be used without it. When used without it, there is an implicit single, aggregated group.
The results you get may depend on your DBMS, but here’s an example that was done in SQL Server.
SELECT
SUM(Population) AS Population
FROM City
HAVING SUM(Population) > 2000000;
Result:
+--------------+ | Population | |--------------| | 1429559884 | +--------------+
This example simply returns the aggregate population of all cities in the table.
Here’s what happens if we swap the greater than operator (>
) with a less than operator (<
).
SELECT
SUM(Population) AS Population
FROM City
HAVING SUM(Population) < 2000000;
Result:
(0 rows affected)