他のほとんどのリレーショナルデータベース製品同様、 PostgreSQL は集約関数をサポートします。集約関数は複数の入力行から 1 つの結果を計算します。例えば、行の集合に対して、 count(総数)、sum(総和)、 avg (平均)、max (最大)、 min (最小) といった演算を行う集約があります。
例えば、以下を使用して全ての都市の最低温度からもっとも高い温度を求めることができます。
SELECT max(temp_lo) FROM weather;
max ----- 46 (1 row)
どの都市のデータなのかを知りたいとしたら、どのようにすればいいのでしょうか。以下のようなコマンドでしょうか。
SELECT city FROM weather WHERE temp_lo = max(temp_lo); 間違い
しかし、max 集約を WHERE で使用することができませんので、このコマンドは動作しません(WHERE 句はどの行を集約処理に渡すのかを決定するものであり、従って、集約関数の演算を行う前に評価されます。このためにこの制限があります)。しかしこの問い合わせのケースはよくありますので、問い合わせを書き直すことで、意図した結果が得られます。これには以下のような副問い合わせ を使用します。
SELECT city FROM weather WHERE temp_lo = (SELECT max(temp_lo) FROM weather);
city --------------- San Francisco (1 row)
副問い合わせはは、外側の問い合わせで起こることとは別々に集約を計算する、独立した演算ですので、この問い合わせは問題ありません。
また、GROUP BY と組み合わせた集約は非常に役に立ちます。例えば、以下のコマンドで都市毎に最低気温の最大値を求めることができます。
SELECT city, max(temp_lo) FROM weather GROUP BY city;
city | max ---------------+----- Hayward | 37 San Francisco | 46 (2 rows)
ここには都市毎に 1 行の出力があります。それぞれの集約結果は都市に一致するテーブル行全体に対する演算結果です。以下のように、HAVING を使用すると、グループ化された行にフィルタを掛けることができます。
SELECT city, max(temp_lo) FROM weather GROUP BY city HAVING max(temp_lo) < 40;
city | max ---------+----- Hayward | 37 (1 row)
このコマンドは上と同じ計算を行うものですが、全ての temp_lo の値が 40 未満の都市のみを出力します。最後になりますが、 "S" から始まる名前の都市のみを対象にしたい場合は、以下を行います。
SELECT city, max(temp_lo) FROM weather WHERE city LIKE 'S%' GROUP BY city HAVING max(temp_lo) < 40;
集約と SQL の WHERE と HAVING 句の間の相互作用を理解することが重要です。 WHERE と HAVING の基本的な違いを以下に記します。WHERE は、グループや集約を演算する前に入力行を選択します(従って、これはどの行を使用して集約演算を行うかを制御します)。一方、 HAVING は、グループと集約を演算した後に、グループ化された行を選択します。従って、WHERE 句は集約関数を持つことはできません。集約を使用して、どの行をその集約の入力にするのかを決定することは意味を成しません。一方で、HAVING 句は常に集約関数を持ちます(厳密にいうと、集約を使用しない HAVING 句を書くことができますが、無駄です。同じ条件は WHERE の段階でもっと効率良く使用できます)。
WHERE 内に都市名制限を適用できることに注目して下さい。集約を行う必要がないからです。WHERE の検査で失敗する全ての行に対するグループ化や集約演算が行われませんので、HAVING に制限を追加するよりもより効率的です。