集約関数っていうのは、複数行のデータをまとめて処理して、1つの結果を返す関数のことだよ。PostgreSQLだと、よく使う集約関数はこんな感じ:
SUM()— データの合計。AVG()— 平均値を出す。MIN()— 最小値を探す。MAX()— 最大値を探す。COUNT()— 行数を数える。
一見シンプルで、カラムや式を関数に渡せば結果が返ってくる。でも、もしカラムにNULLがあったらどうなる?
集約でのNULLの挙動:ざっくりまとめ
ここからが面白いところ:
SUM()とAVG()はNULLを無視する。1つでもNULLがあれば、その行は計算に入らない。まあ、パーティーに来なかった人の分は合計に入らないって感じ。平均も、値が足りないなら計算できないしね。MIN()とMAX()もNULLをスルー。NULLじゃないデータだけで最小・最大を探す。だから、誕生日を入力し忘れた一番若い社員を探しても、NULLは選ばれないよ。COUNT(*)は全行をカウントする。NULLがあっても関係なし。でもCOUNT(column)は、そのカラムに値がある行だけ数える。つまりNULLは無視される。
じゃあ、実際に例で見てみよう。
NULL入り集約関数の使い方例
例えば、students_scoresテーブルには学生のテストスコアが入ってる:
| student_id | name | score |
|---|---|---|
| 1 | アリサ | 85 |
| 2 | ボブ | NULL |
| 3 | チャーリー | 92 |
| 4 | ダナ | NULL |
| 5 | エレナ | 74 |
じゃあ、いくつかクエリを投げて結果を見てみよう:
- 全スコアの合計:
SUM()
SELECT SUM(score) AS total_score
FROM students_scores;
結果:
| total_score |
|---|
| 251 |
見ての通り、NULLは合計に入ってない。アリサ(85)、チャーリー(92)、エレナ(74)で合計251。ボブとダナはスルーされた。
- 平均スコア:
AVG()
SELECT AVG(score) AS average_score
FROM students_scores;
結果:
| average_score |
|---|
| 83.67 |
ここでもNULLは無視されて、スコアがある人だけで平均を計算:(85 + 92 + 74) / 3 = 83.67。
- 最小・最大スコア:
MIN()とMAX()
SELECT
MIN(score) AS min_score,
MAX(score) AS max_score
FROM students_scores;
結果:
| min_score | max_score |
|---|---|
| 74 | 92 |
これもシンプル。NULLは無視されて、最小は74、最大は92。
- 行数カウント:
COUNT(*)vsCOUNT(column)
SELECT
COUNT(*) AS total_rows,
COUNT(score) AS non_null_scores
FROM students_scores;
結果:
| total_rows | non_null_scores |
|---|---|
| 5 | 3 |
COUNT(*)はscoreがNULLでも全行をカウント。COUNT(score)はscoreに値がある行だけカウント。
実践ケース
いくつか実践的な例も見てみよう。
例1:給与がある・ない社員のカウント
例えば、employeesテーブルに給与が入ってるとする。
| id | name | salary |
|---|---|---|
| 1 | Alex Lin | 50000 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 60000 |
| 4 | Otto Art | NULL |
| 5 | Liam Park | 55000 |
給与を入力した社員と、してない社員の数を知りたい。
SELECT
COUNT(*) AS total_employees,
COUNT(salary) AS employees_with_salary,
COUNT(*) - COUNT(salary) AS employees_without_salary
FROM employees;
ここで:
COUNT(*)は社員の総数。COUNT(salary)は給与を入力した社員の数。- 給与なし社員は、総数から給与あり社員を引くだけ。
結果
| total_employees | employees_with_salary | employees_without_salary |
|---|---|---|
| 5 | 3 | 2 |
例2:商品価格の平均(未入力データあり)
君は魔法ショップのオーナーで、productsテーブルのpriceカラムには一部商品で価格が未入力。
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 150 |
| 2 | Enchanted Cloak | NULL |
| 3 | Potion Bottle | 75 |
| 4 | Spell Book | 200 |
| 5 | Crystal Ball | NULL |
価格が入力されてる商品のみで平均を知りたい。
SELECT AVG(price) AS average_price
FROM products;
結果:
| average_price |
|---|
| 141.6667 |
もし価格未入力の商品にデフォルト値(例えば0)を入れたいなら、次のレクチャーで出てくるCOALESCE()関数を使えばOK!
例3:学生の最小・最大年齢を探す
studentsテーブルには学生の年齢が入ってるけど、一部は年齢不明(NULL)。
| id | name | age |
|---|---|---|
| 1 | Alex Lin | 20 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 19 |
| 4 | Otto Art | 22 |
| 5 | Liam Park | NULL |
一番若い&一番年上の学生を知りたい。
SELECT
MIN(age) AS youngest_student,
MAX(age) AS eldest_student
FROM students;
結果:
| youngest_student | eldest_student |
|---|---|
| 19 | 22 |
このクエリは年齢が入力されてる学生だけで最小・最大を返す。NULLはまたスルー。
注意点・落とし穴
NULL入り集約を使うときは、次のポイントに気をつけて:
SUM()やAVG()ではNULLは無視される。空の値を計算に入れたくないときに便利。- カラムに
NULLがある行もカウントしたいならCOUNT(*)を使おう。 MIN()やMAX()もNULLは結果に影響しない。ただし、カラムが全部NULLなら結果もNULLになる。
NULLと付き合うコツ
- 目的に合わせて考えよう。 クエリで
NULLを無視していいかどうかをちゃんと考えよう。AVG()みたいに無視したいときもあれば、全体数を数えたいときはNULLも含めたい。 COALESCE()を活用しよう。 計算でNULLをデフォルト値に置き換えたいときはCOALESCE()が便利(これは次のレクチャーで!)。COUNT(*)とCOUNT(column)の違いに注意。 これ、初心者がよくやるミス。前者は全行、後者は値がある行だけカウント。
これで、ひっそりと存在感を放つNULLが集約にどう影響するかバッチリ!これを知ってれば、思わぬ落とし穴も回避できるし、NULLをうまく使いこなせるよ。次のレクチャーでは、COALESCE()という強力な武器でNULLをもっと自在に操る方法を学ぼう!
GO TO FULL VERSION