Prepare Fake Data
CREATE TABLE vaccines (country text, type text, effectiveness smallint) ;
DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Japan', 'Phizer', 90+round(10*random()) ) ;
end LOOP;
end;
$$;
DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Japan', 'Moderna', 90+round(10*random()) ) ;
END LOOP;
END;
$$;
DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Australia', 'Phizer', 90+round(10*random()) ) ;
END LOOP;
END;
$$;
DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Australia', 'Moderna', 90+round(10*random()) ) ;
END LOOP;
END;
$$;
Then you can use GROUP BY
with ROLLUP
to let Postgres return average for you:
SELECT country, type, AVG(effectiveness)
FROM vaccines
WHERE country IN ('Japan', 'Australia')
GROUP BY ROLLUP (country, type)
ORDER BY country ASC
;
country | type | avg
-----------+---------+---------------------
Australia | | 94.9977000000000000
Australia | Moderna | 94.9611000000000000
Australia | Phizer | 95.0343000000000000
Japan | Moderna | 94.9578000000000000
Japan | Phizer | 95.0509000000000000
Japan | | 95.0043500000000000
| | 95.0010250000000000
Note this is fake return data
The plan looks like and is using a MixedAggregate
:
QUERY PLAN
---------------------------------------------------------------------------
Sort (cost=1336.19..1336.20 rows=7 width=47)
Sort Key: country
-> MixedAggregate (cost=0.00..1336.09 rows=7 width=47)
Hash Key: country, type
Hash Key: country
Group Key: ()
-> Seq Scan on vaccines (cost=0.00..736.00 rows=40000 width=17)
Filter: (country = ANY ('{Japan,Australia}'::text[]))
(8 rows)
Time: 0.437 m
You see PostgreSQL inserts an extra row for avg for Japan and another one for Australia. And a row in the end for both Japan and Australia.
Related functions are: GROUPING SETS
and CUBE
.