Last active
May 6, 2017 20:33
-
-
Save rmorenobello/c94e28818bd7cb6930e71380c7f915ea to your computer and use it in GitHub Desktop.
Calculate Multiple Aggregate Functions (with diff combinations of fixed WHERE conditions) in a Single Query
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- https://dzone.com/articles/how-to-calculate-multiple-aggregate-functions-in-a?edition=297992 | |
-- Calculate Multiple Aggregate Functions (diff WHERE conditions) in a Single Query | |
-- ***************** PROBLEM to solve: | |
-- Number of films with a given length / language_id | |
SELECT count(*) | |
FROM film | |
WHERE length BETWEEN 120 AND 150 | |
AND language_id = 1; | |
-- Number of films with a given length / rating | |
SELECT count(*) | |
FROM film | |
WHERE length BETWEEN 120 AND 150 | |
AND rating = 'PG'; | |
-- Number of films with a given language_id / rating | |
SELECT count(*) | |
FROM film | |
WHERE language_id = 1 | |
AND rating = 'PG'; | |
-- Number of films with a given length / language_id / rating | |
SELECT count(*) | |
FROM film | |
WHERE length BETWEEN 120 AND 150 | |
AND language_id = 1 | |
AND rating = 'PG'; | |
-- we observe always combinations of 3 basic conditions: | |
length BETWEEN 120 AND 150 | |
language_id = 1 | |
rating = 'PG'; | |
-- ************** SOLUTION 1 | |
-- With DERIVED TABLE with nulls for false | |
/* | |
Create a derived table with the different predicates to combine, which should return null when false and 1 when true. | |
Then we can count each case as nulls won't sum. | |
The trick to combine cases is the fact that | |
1+null=null | |
so we can also do count(length + language_id) and it will only count when both conditions are true. | |
*/ | |
SELECT | |
count(*), | |
count(length), | |
count(language_id), | |
count(rating), | |
-- 1+null=null trick | |
count(length + language_id), | |
count(length + rating), | |
count(language_id + rating), | |
count(length + language_id + rating) | |
FROM ( | |
-- Heart of the trick (notice no use of ELSE so we get those usefull NULLs): | |
SELECT | |
CASE WHEN length BETWEEN 120 AND 150 THEN 1 END length, | |
CASE WHEN language_id = 1 THEN 1 END language_id, | |
CASE WHEN rating = 'PG' THEN 1 END rating | |
FROM film | |
) film | |
; | |
/* result: | |
col1 col2 col3 col4 col5 col6 col7 col8 | |
1000 224 1000 194 224 43 194 43 | |
*/ | |
-- ************** SOLUTION 2 | |
-- With FILTER clause syntax (PostgreSQL and HSQLDB and in the SQL standard) | |
SELECT | |
count(*), | |
count(*) FILTER (WHERE length BETWEEN 120 AND 150), | |
count(*) FILTER (WHERE language_id = 1), | |
count(*) FILTER (WHERE rating = 'PG'), | |
count(*) FILTER ( | |
WHERE length BETWEEN 120 AND 150 AND language_id = 1), | |
count(*) FILTER ( | |
WHERE length BETWEEN 120 AND 150 AND rating = 'PG'), | |
count(*) FILTER ( | |
WHERE language_id = 1 AND rating = 'PG'), | |
count(*) FILTER ( | |
WHERE length BETWEEN 120 AND 150 | |
AND language_id = 1 AND rating = 'PG') | |
FROM film | |
; | |
-- *************** How the derived table variant works: | |
-- the derived table: | |
SELECT | |
CASE WHEN length BETWEEN 120 AND 150 THEN 1 END length, | |
CASE WHEN language_id = 1 THEN 1 END language_id, | |
CASE WHEN rating = 'PG' THEN 1 END rating | |
FROM film | |
; | |
/* | |
length language_id rating | |
--------------------------- | |
NULL 1 1 | |
NULL 1 NULL | |
NULL 1 NULL | |
NULL 1 NULL | |
1 1 NULL | |
NULL 1 1 | |
NULL 1 NULL | |
... | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment