SQLの集計関数とは、複数の行をまとめて1つの値(合計・平均・最大・最小・件数など)を返す関数です。 代表は SUM・AVG・MAX・MIN・COUNT の5つで、どのデータベースでも同じ名前で使えます。

5つとも書き方はシンプルですが、NULL(値が入っていない状態)があると結果が変わる点でつまずきやすい関数でもあります。この記事では、NULL を含む6行のサンプル表を用意し、COUNT(*)・COUNT(列)・COUNT(DISTINCT 列)・SUM・AVG・MAX・MIN の結果を1つの表で見比べられるようにしました。すべて MySQL 8.0.46・PostgreSQL 16.15・SQLite 3.45.1 で実際に実行した結果です。文字列をつなげる集計(GROUP_CONCAT / STRING_AGG / LISTAGG)などDBごとに名前が違う関数の対応表、GROUP BY・HAVING・ウィンドウ関数としての使い方まで1ページにまとめています。

集計関数の早見表

関数 返すもの NULL の扱い 対象が0件のとき 使える型
COUNT(*) 行数 NULL の行も数える 0 すべて
COUNT(列) 列が NULL でない行の数 NULL を数えない 0 すべて
COUNT(DISTINCT 列) 重複を除いた値の種類数 NULL を数えない 0 すべて
SUM(列) 合計 NULL を飛ばして足す NULL 数値
AVG(列) 平均 NULL の行を分母からも外す NULL 数値
MAX(列) 最大値 NULL を無視 NULL 数値・文字列・日付
MIN(列) 最小値 NULL を無視 NULL 数値・文字列・日付

サンプル表(6行、金額に NULL が2行)での実際の結果は次のとおりです。

式 結果 読み方
COUNT(*) 6 全部で6行
COUNT(amount) 4 金額が入っているのは4行
COUNT(DISTINCT amount) 3 金額の種類は 1000・2000・3000 の3つ
SUM(amount) 8000 1000+3000+2000+2000
AVG(amount) 2000 8000 ÷ 4(6 で割らない)
MAX(amount) 3000 いちばん大きい金額
MIN(amount) 1000 いちばん小さい金額(NULL は最小にならない)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL集計関数一覧|SUM・AVG・MAX・MIN・COUNTとNULLの扱い」

この記事で使うサンプルテーブル

売上を記録する sales テーブルです。NULL の動きが見えるよう、金額が空の行と、カテゴリも金額も空の行を入れてあります。次のSQLは MySQL・PostgreSQL・SQLite のどれでもそのまま動きます。

CREATE TABLE sales (
  id INT PRIMARY KEY,
  staff VARCHAR(20),
  category VARCHAR(20),
  amount INT
);

INSERT INTO sales VALUES
(1,'Aoki','book',1000),
(2,'Aoki','book',3000),
(3,'Aoki','pen',NULL),
(4,'Baba','pen',2000),
(5,'Baba','pen',2000),
(6,'Chiba',NULL,NULL);
id staff category amount
1 Aoki book 1000
2 Aoki book 3000
3 Aoki pen NULL
4 Baba pen 2000
5 Baba pen 2000
6 Chiba NULL NULL

7つの結果を1つのSQLで見比べる

まず、主な集計関数を1つの SELECT に並べて実行します。

SELECT COUNT(*)               AS cnt_all,
       COUNT(amount)          AS cnt_col,
       COUNT(DISTINCT amount) AS cnt_dist,
       SUM(amount)            AS sum_amt,
       AVG(amount)            AS avg_amt,
       MAX(amount)            AS max_amt,
       MIN(amount)            AS min_amt
FROM sales;

実行結果(MySQL 8.0.46):

+---------+---------+----------+---------+-----------+---------+---------+
| cnt_all | cnt_col | cnt_dist | sum_amt | avg_amt   | max_amt | min_amt |
+---------+---------+----------+---------+-----------+---------+---------+
|       6 |       4 |        3 |    8000 | 2000.0000 |    3000 |    1000 |
+---------+---------+----------+---------+-----------+---------+---------+

PostgreSQL 16.15 では avg_amt が 2000.0000000000000000、SQLite 3.45.1 では 2000.0 と表示され、値そのものは3つのDBで同じでした。違うのは平均の表示桁数だけです。

DB AVG(amount) の表示
MySQL 8.0.46 2000.0000
PostgreSQL 16.15 2000.0000000000000000
SQLite 3.45.1 2000.0

表示をそろえたいときは ROUND(AVG(amount), 1) のように丸めます(PostgreSQL 16.15 で 2000.0 になることを確認)。

COUNTの3つの書き方と数え方の差

COUNT は、かっこの中に何を書くかで数える対象が変わります。

書き方 数えるもの サンプルでの結果
COUNT(*) 行そのもの。列が NULL でも数える 6
COUNT(amount) amount が NULL でない行 4
COUNT(DISTINCT amount) amount の値の種類(NULL は除く) 3
COUNT(1) COUNT(*) と同じく行数 6

「行数が知りたいときは COUNT(*)、値が入っている件数が知りたいときは COUNT(列)」と使い分けます。COUNT(*) と COUNT(列) の差(6 − 4 = 2)は、その列が NULL の行数です。未入力の件数を調べるときにそのまま使えます。

-- 金額が未入力の行数を出す
SELECT COUNT(*) - COUNT(amount) AS null_rows FROM sales;

COUNT(DISTINCT 列) は重複を除いて数えます。サンプルでは 2000 が2行ありますが、1種類として数えられるので 3 になります。「何人の担当者が売上を登録したか」「何種類のカテゴリがあるか」といった問いに向いています。

AVGはNULLの行を分母から外す

AVG は「合計 ÷ 件数」ですが、このときの件数は NULL でない行の数です。サンプルでは 8000 ÷ 4 = 2000 になり、6 行で割った 1333.33… にはなりません。

「未入力は 0 として平均に入れたい」場合は、COALESCE(NULL を別の値に置き換える関数)で 0 にしてから平均します。3つの計算方法を並べてみます。

SELECT AVG(amount)               AS avg_skip_null,
       AVG(COALESCE(amount, 0))  AS avg_null_as_0,
       SUM(amount) / COUNT(*)    AS sum_div_rows
FROM sales;

実行結果(MySQL 8.0.46):

+---------------+---------------+--------------+
| avg_skip_null | avg_null_as_0 | sum_div_rows |
+---------------+---------------+--------------+
|     2000.0000 |     1333.3333 |    1333.3333 |
+---------------+---------------+--------------+

実行結果(PostgreSQL 16.15):

     avg_skip_null     |     avg_null_as_0     | sum_div_rows
-----------------------+-----------------------+--------------
 2000.0000000000000000 | 1333.3333333333333333 |         1333
(1 row)
式 分子 分母 結果
AVG(amount) 8000 4(NULL でない行) 2000
AVG(COALESCE(amount, 0)) 8000 6(全行) 1333.33…
SUM(amount) / COUNT(*) 8000 6(全行) MySQL は 1333.3333、PostgreSQL と SQLite は 1333

3つめの式で DB ごとの差が出ました。PostgreSQL と SQLite は整数どうしの割り算の結果を整数にするため、小数点以下が切り捨てられて 1333 になります。小数まで欲しいときは SUM(amount) * 1.0 / COUNT(*) のように片方を小数にするか、AVG(COALESCE(amount, 0)) を使うと、どのDBでも同じ結果になります。

どちらの平均が正しいかは、NULL の意味で決まります。

NULL の意味 向いている式
まだ入力されていない・対象外(平均に入れたくない) AVG(amount)
売上が 0 だった(平均に入れたい) AVG(COALESCE(amount, 0))

対象が0件のときSUMはNULLになる

WHERE で絞り込んだ結果、1行も残らなかった場合の動きです。存在しない担当者で集計してみます。

SELECT COUNT(*)                  AS cnt,
       SUM(amount)               AS sum_amt,
       AVG(amount)               AS avg_amt,
       MAX(amount)               AS max_amt,
       COALESCE(SUM(amount), 0)  AS sum_or_0
FROM sales
WHERE staff = 'Nobody';

実行結果(MySQL 8.0.46。PostgreSQL 16.15・SQLite 3.45.1 でも同じ内容):

+-----+---------+---------+---------+----------+
| cnt | sum_amt | avg_amt | max_amt | sum_or_0 |
+-----+---------+---------+---------+----------+
|   0 |    NULL |    NULL |    NULL |        0 |
+-----+---------+---------+---------+----------+

COUNT だけが 0 を返し、SUM・AVG・MAX・MIN は NULL を返します。 「合計は 0 になるはず」と考えてアプリ側で数値として受け取ると、NULL の扱いで想定外の動きになることがあります。

対処は、集計関数の外側を COALESCE で包むことです。

-- 0件でも 0 を返す
SELECT COALESCE(SUM(amount), 0) AS total FROM sales WHERE staff = 'Nobody';

COALESCE を置く場所で意味が変わるので、整理しておきます。

書き方 意味 0件のとき NULL の行があるとき
SUM(amount) そのまま合計 NULL NULL を飛ばして合計
COALESCE(SUM(amount), 0) 合計が NULL なら 0 にする 0 NULL を飛ばして合計
SUM(COALESCE(amount, 0)) 各行の NULL を 0 にしてから合計 NULL のまま 合計は同じ

0件への備えとして効くのは2行目の「外側に置く」形です。3行目は各行の値を置き換えているだけなので、行が1つもなければ結果は NULL のままです。

同じことが、GROUP BY でグループの中の値がすべて NULL のときにも起こります(次の章の Chiba の行)。

GROUP BYでグループごとに集計する

GROUP BY を付けると、指定した列の値ごとに行をまとめ、グループごとに集計関数が計算されます。

SELECT staff,
       COUNT(*)      AS cnt,
       COUNT(amount) AS cnt_amt,
       SUM(amount)   AS sum_amt,
       AVG(amount)   AS avg_amt
FROM sales
GROUP BY staff
ORDER BY staff;

実行結果(MySQL 8.0.46):

+-------+-----+---------+---------+-----------+
| staff | cnt | cnt_amt | sum_amt | avg_amt   |
+-------+-----+---------+---------+-----------+
| Aoki  |   3 |       2 |    4000 | 2000.0000 |
| Baba  |   2 |       2 |    4000 | 2000.0000 |
| Chiba |   1 |       0 |    NULL |      NULL |
+-------+-----+---------+---------+-----------+
  • Aoki は3行ありますが、金額が入っているのは2行なので、平均は 4000 ÷ 2 = 2000 です
  • Chiba は行が1つあるのに、金額が NULL だけなので SUM も AVG も NULL になります。0 と表示したいときは COALESCE(SUM(amount), 0) にします

GROUP BYの列がNULLのとき

グループ分けに使う列が NULL の行は、NULL どうしで1つのグループにまとめられます。

SELECT category, COUNT(*) AS cnt, SUM(amount) AS sum_amt
FROM sales
GROUP BY category
ORDER BY category;

実行結果(PostgreSQL 16.15):

 category | cnt | sum_amt
----------+-----+---------
 book     |   2 |    4000
 pen      |   3 |    4000
          |   1 |
(3 rows)

カテゴリが空の行が1グループとして出ています。なお、ORDER BY での NULL の位置はDBによって異なり、PostgreSQL では最後、MySQL 8.0.46 と SQLite 3.45.1 では先頭に表示されました。

SELECTに書ける列のルール

GROUP BY を使うとき、SELECT に書けるのは「GROUP BY に書いた列」と「集計関数」です。それ以外の列を書くと、グループの中のどの行の値を出せばよいかが決まらないため、エラーになります。

-- category は GROUP BY にも集計関数にも入っていない
SELECT staff, category, SUM(amount) FROM sales GROUP BY staff;

PostgreSQL 16.15 のエラー文:

ERROR:  column "sales.category" must appear in the GROUP BY clause or be used in an aggregate function

MySQL 8.0.46 のエラー文(既定の sql_mode に ONLY_FULL_GROUP_BY が含まれる場合):

ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 't200.sales.category' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

直し方は目的によって3つあります。

やりたいこと 直し方
担当者×カテゴリごとに集計したい GROUP BY staff, category にする
カテゴリは代表の1つでよい MAX(category) や MIN(category) で包む
カテゴリを全部つなげて見たい 文字列連結の集計関数を使う(後述)

HAVINGで集計結果を絞り込む

「合計が 4000 以上の担当者だけ」のように、集計した結果に条件を付けるときは HAVING を使います。

SELECT staff, SUM(amount) AS sum_amt
FROM sales
GROUP BY staff
HAVING SUM(amount) >= 4000
ORDER BY staff;

実行結果(3つのDBで同じ):

 staff | sum_amt
-------+---------
 Aoki  |    4000
 Baba  |    4000
(2 rows)

Chiba は合計が NULL なので、NULL >= 4000 が真にならず、結果から外れます。

WHEREに集計関数を書くとエラーになる

WHERE は「集計する前の行」を絞る場所なので、集計関数は書けません。

SELECT staff, amount FROM sales WHERE amount > AVG(amount);
-- PostgreSQL 16.15
ERROR:  aggregate functions are not allowed in WHERE

-- MySQL 8.0.46
ERROR 1111 (HY000): Invalid use of group function

「平均より大きい行」「最大の行」を取り出したいときは、サブクエリ(SQLの中に入れた別のSELECT)で先に集計します。

-- 金額が最大の行を取り出す
SELECT staff, amount FROM sales
WHERE amount = (SELECT MAX(amount) FROM sales);
 staff | amount
-------+--------
 Aoki  |   3000
(1 row)

WHEREとHAVINGの違い

WHERE HAVING
絞り込む対象 集計前の1行1行 集計後のグループ
集計関数 書けない 書ける
実行される順番 GROUP BY より前 GROUP BY より後
例 WHERE category = 'pen' HAVING COUNT(*) >= 2

SELECT 文は、書く順番とは別に、次の順番で処理されます。

flowchart LR
    A[FROM] --> B[WHERE] --> C[GROUP BY] --> D[HAVING] --> E[SELECT] --> F[ORDER BY]

集計に関係しない条件(カテゴリや日付での絞り込み)は WHERE に書くと、集計する行数が先に減るので効率的です。

条件つきの集計(CASEとFILTER)

「2000 以上の件数」と「pen の合計」のように、条件の違う集計を1回のSQLで並べたいときは、集計関数の中に CASE を書きます。

SELECT COUNT(CASE WHEN amount >= 2000 THEN 1 END)                  AS ge2000,
       SUM(CASE WHEN category = 'pen' THEN amount ELSE 0 END)      AS pen_total
FROM sales;

実行結果(3つのDBで同じ):

 ge2000 | pen_total
--------+-----------
      3 |      4000
(1 row)

COUNT(CASE WHEN … THEN 1 END) は、条件に合わない行が NULL になり、COUNT が NULL を数えない性質を利用しています。NULL の扱いを知っていると、こうした書き方の意味も読み取れるようになります。

PostgreSQL では、同じことを FILTER で書けます。

-- PostgreSQL 16.15 で確認
SELECT staff, COUNT(*) FILTER (WHERE amount >= 2000) AS ge2000
FROM sales GROUP BY staff ORDER BY staff;
 staff | ge2000
-------+--------
 Aoki  |      1
 Baba  |      2
 Chiba |      0
(3 rows)

MAXとMINは文字列や日付にも使える

MAX と MIN は数値以外にも使えます。文字列では辞書順、日付では新しい・早い順で比べます。

SELECT SUM(DISTINCT amount) AS sum_dist,
       MAX(staff)           AS max_staff,
       MIN(staff)           AS min_staff
FROM sales;
 sum_dist | max_staff | min_staff
----------+-----------+-----------
     6000 | Chiba     | Aoki
(1 row)

SUM(DISTINCT amount) は重複を除いて合計するため、1000+2000+3000 = 6000 です。2000 が1回しか足されない点に注意して使います。

型 MAX が返すもの MIN が返すもの
数値 いちばん大きい値 いちばん小さい値
文字列 辞書順で最後 辞書順で最初
日付・日時 いちばん新しい日時 いちばん早い日時

MIN は NULL を「最小」とは見なしません。NULL は比べる対象から外れます。

ウィンドウ関数として集計関数を使う

GROUP BY は行をまとめてしまいますが、OVER を付けると、元の行を残したまま集計結果を横に並べられます。 これがウィンドウ関数としての使い方です。

SELECT id, staff, amount,
       SUM(amount) OVER (PARTITION BY staff) AS staff_total,
       SUM(amount) OVER (ORDER BY id)        AS running,
       COUNT(*)    OVER ()                   AS all_rows
FROM sales
ORDER BY id;

実行結果(MySQL 8.0.46。PostgreSQL 16.15・SQLite 3.45.1 でも同じ内容):

+----+-------+--------+-------------+---------+----------+
| id | staff | amount | staff_total | running | all_rows |
+----+-------+--------+-------------+---------+----------+
|  1 | Aoki  |   1000 |        4000 |    1000 |        6 |
|  2 | Aoki  |   3000 |        4000 |    4000 |        6 |
|  3 | Aoki  |   NULL |        4000 |    4000 |        6 |
|  4 | Baba  |   2000 |        4000 |    6000 |        6 |
|  5 | Baba  |   2000 |        4000 |    8000 |        6 |
|  6 | Chiba |   NULL |        NULL |    8000 |        6 |
+----+-------+--------+-------------+---------+----------+
OVER の中身 意味 列
PARTITION BY staff 担当者ごとの合計を各行に付ける staff_total
ORDER BY id 先頭からその行までの累計 running
空の () 全体の集計を各行に付ける all_rows

ここでも NULL の扱いは同じです。id=3 の累計は NULL を飛ばして 4000 のまま、Chiba の担当者合計は値がすべて NULL なので NULL になっています。

GROUP BY ウィンドウ関数(OVER)
結果の行数 グループの数に減る 元の行数のまま
元の列 GROUP BY の列だけ出せる すべての列を出せる
向いている用途 担当者別の合計表 明細に合計・累計・構成比を添える

DB別の関数名対応表

SUM・AVG・MAX・MIN・COUNT はどのDBでも共通ですが、それ以外の集計関数は名前や書き方が異なります。MySQL・PostgreSQL・SQLite は今回実行した書き方、SQL Server と Oracle は今回の環境で動かせなかったため未実行(一般に知られた仕様)です。

文字列をつなげる集計

DB 関数 書き方の例 確認
MySQL 8.0 GROUP_CONCAT GROUP_CONCAT(category ORDER BY id SEPARATOR ',') 実行済み
PostgreSQL 16 STRING_AGG STRING_AGG(category, ',' ORDER BY id) 実行済み
SQLite 3.45 GROUP_CONCAT(STRING_AGG の名前でも動いた) GROUP_CONCAT(category, ',') 実行済み
SQL Server STRING_AGG(2017 以降) STRING_AGG(category, ',') WITHIN GROUP (ORDER BY id) 未実行
Oracle Database LISTAGG LISTAGG(category, ',') WITHIN GROUP (ORDER BY id) 未実行
-- MySQL 8.0.46
SELECT staff,
       GROUP_CONCAT(category ORDER BY id SEPARATOR ',') AS cats,
       GROUP_CONCAT(DISTINCT category)                  AS dcats
FROM sales GROUP BY staff ORDER BY staff;
+-------+---------------+----------+
| staff | cats          | dcats    |
+-------+---------------+----------+
| Aoki  | book,book,pen | book,pen |
| Baba  | pen,pen       | pen      |
| Chiba | NULL          | NULL     |
+-------+---------------+----------+
-- PostgreSQL 16.15(同じ結果。Chiba の行は空欄=NULL で表示)
SELECT staff,
       STRING_AGG(category, ',' ORDER BY id) AS cats,
       STRING_AGG(DISTINCT category, ',')    AS dcats
FROM sales GROUP BY staff ORDER BY staff;

文字列の連結でも NULL は飛ばされ、すべて NULL のグループは結果も NULL になります。数値の集計と同じ考え方です。

そのほかの集計関数

やりたいこと MySQL 8.0 PostgreSQL 16 SQLite 3.45 SQL Server(未実行) Oracle(未実行)
標準偏差 STDDEV / STDDEV_SAMP STDDEV 標準では用意されていない STDEV STDDEV
分散 VARIANCE / VAR_SAMP VARIANCE 標準では用意されていない VAR VARIANCE
値を配列にまとめる JSON_ARRAYAGG ARRAY_AGG json_group_array — —
条件つき集計 CASE 式 CASE 式または FILTER CASE 式または FILTER CASE 式 CASE 式
0件でも 0 を返す合計 COALESCE(SUM(x),0) COALESCE(SUM(x),0) TOTAL(x) または COALESCE COALESCE(SUM(x),0) COALESCE(SUM(x),0) または NVL
小計・総計の行を足す GROUP BY … WITH ROLLUP GROUP BY ROLLUP(…) — GROUP BY ROLLUP(…) GROUP BY ROLLUP(…)

この表のうち今回実行したのは、MySQL と PostgreSQL の STDDEV・VARIANCE・ROLLUP、MySQL の JSON_ARRAYAGG、PostgreSQL の ARRAY_AGG と FILTER、SQLite の json_group_array・FILTER・TOTAL、3つのDB共通の CASE 式と COALESCE です。SQL Server と Oracle の列、および「標準では用意されていない」と書いたセルは未実行です。

実行してわかった差を2つ紹介します。

1. 同じ STDDEV でも、MySQL と PostgreSQL で結果が違いました。

DB STDDEV(amount) VARIANCE(amount) 計算方法
MySQL 8.0.46 707.1067811865476 500000 母集団(件数 n で割る)
PostgreSQL 16.15 816.496580927726 666666.666666666667 標本(n − 1 で割る)

どちらの計算かを明示したいときは、STDDEV_POP(母集団)・STDDEV_SAMP(標本)のように名前で指定すると、DBを移っても意味が変わりません。MySQL 8.0.46 と PostgreSQL 16.15 の両方で、次の結果が一致することを確認しました。

SELECT STDDEV_POP(amount) AS pop, STDDEV_SAMP(amount) AS samp FROM sales;
+-------------------+------------------+
| pop               | samp             |
+-------------------+------------------+
| 707.1067811865476 | 816.496580927726 |
+-------------------+------------------+

値を配列にまとめる集計では、NULL も要素として残る点が SUM や COUNT と異なります。MySQL 8.0.46 の JSON_ARRAYAGG(amount) は [1000, 3000, null, 2000, 2000, null]、PostgreSQL 16.15 の ARRAY_AGG(amount) を担当者ごとに実行すると Aoki は {1000,3000,NULL} になりました。

2. SQLite の TOTAL は、0件のとき NULL ではなく 0.0 を返します。

-- SQLite 3.45.1
SELECT TOTAL(amount), SUM(amount) FROM sales WHERE staff = 'Nobody';
TOTAL(amount)|SUM(amount)
0.0|

小計と総計を足すROLLUP

ROLLUP を使うと、グループごとの集計に加えて総計の行が付きます。

-- MySQL 8.0.46
SELECT staff, SUM(amount) FROM sales GROUP BY staff WITH ROLLUP;
+-------+-------------+
| staff | SUM(amount) |
+-------+-------------+
| Aoki  |        4000 |
| Baba  |        4000 |
| Chiba |        NULL |
| NULL  |        8000 |
+-------+-------------+

最後の staff が NULL の行が総計です。PostgreSQL では GROUP BY ROLLUP(staff, category) と書き、担当者ごとの小計と全体の総計(8000)が出ることを確認しました。

結果が想定と違うときのチェックリスト

症状 原因 直し方
平均が思ったより大きい AVG が NULL の行を分母から外している 0 として扱うなら AVG(COALESCE(列, 0))
件数が行数より少ない COUNT(列) が NULL を数えていない 行数なら COUNT(*)
合計が NULL になる 対象が0件、または値がすべて NULL COALESCE(SUM(列), 0)
割り算の結果に小数が出ない 整数どうしの割り算(PostgreSQL・SQLite) * 1.0 を掛けるか AVG を使う
WHERE に集計関数を書いてエラー WHERE は集計前に処理される HAVING かサブクエリに移す
GROUP BY でエラー 1055 など SELECT に GROUP BY にない列がある GROUP BY に足すか集計関数で包む
合計が明細の合計より大きい JOIN で行が増えてから集計している 先に集計してから JOIN する
標準偏差がDBで違う STDDEV の既定の計算方法が違う STDDEV_POP / STDDEV_SAMP を明示

よくある質問

Q. COUNT(*) と COUNT(列) は何が違いますか? A. COUNT(*) は行数を数え、COUNT(列) はその列が NULL でない行だけを数えます。サンプルでは 6 と 4 でした。差の 2 が、その列が NULL の行数です。

Q. AVGでNULLを0として平均に入れるにはどうしますか? A. AVG(COALESCE(列, 0)) と書きます。サンプルでは、そのままの AVG が 2000、COALESCE を入れた AVG が 1333.33… でした。NULL が「未入力」なのか「0」なのかで、どちらを使うかを決めます。

Q. データがないときにSUMを0にしたいです。 A. COALESCE(SUM(列), 0) と、SUM の外側を COALESCE で包みます。SUM(COALESCE(列, 0)) は各行の NULL を 0 にするだけなので、行が1つもないときは NULL のままです。

Q. 集計関数はWHEREで使えますか? A. 使えません。PostgreSQL では「aggregate functions are not allowed in WHERE」、MySQL では「Invalid use of group function」というエラーになります。集計結果で絞るなら HAVING、集計値と各行を比べるならサブクエリを使います。

まとめ

  • 集計関数は SUM・AVG・MAX・MIN・COUNT の5つが基本で、どのDBでも同じ名前で使える
  • COUNT(*) は行数、COUNT(列) は NULL でない行数、COUNT(DISTINCT 列) は値の種類数(サンプルで 6・4・3)
  • AVG は NULL の行を分母から外す。0 として入れたいときは AVG(COALESCE(列, 0))
  • 対象が0件だと COUNT は 0、SUM・AVG・MAX・MIN は NULL。COALESCE(SUM(列), 0) で 0 にできる
  • 集計前の絞り込みは WHERE、集計後の絞り込みは HAVING
  • OVER を付けるとウィンドウ関数になり、明細を残したまま合計や累計を並べられる
  • 文字列連結は GROUP_CONCAT(MySQL・SQLite)/STRING_AGG(PostgreSQL・SQL Server)/LISTAGG(Oracle)とDBで名前が違う

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL集計関数一覧|SUM・AVG・MAX・MIN・COUNTとNULLの扱い」

株式会社RJCでは、SQLの集計関数とNULLの扱いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?