集計ファンクションとは
複数件のデータを集計するためのファンクション(グループ関数とも呼ばれる)。グループ化されたデータに対して、グループごとに1行の結果を返す。
主要な特徴:
-
SELECT句、HAVING句、ORDER BY句でのみ使用可能 -
WHERE句には集計ファンクションを使えない(集計はグループ化後に行われるため) - 集計ファンクションの結果を絞り込む場合は、
HAVING句を使用する
⚠️ 試験ポイント:集計した結果に条件を付けたい場合は
WHEREではなくHAVINGを使う。WHEREは行単位のフィルタリング(グループ化の前)、HAVINGはグループ単位のフィルタリング(グループ化の後)。
集計ファンクション一覧
① SUM()
複数行の列値の合計を求める(数値型のみ対象)。
SELECT SUM(sal) FROM emp;
-
GROUP BYなしの場合、全行が1グループとして合計される -
GROUP BYと組み合わせると、グループ単位での合計が出る
⚠️ 試験ポイント(ORA-00937エラー):
GROUP BYなしで、SUM()などの集計ファンクションと、集計対象外の個別列を同時にSELECTしようとするとエラー(ORA-00937: not a single-group group function)になる。
集計結果と個別列を一緒に取得するにはGROUP BYでその列を指定する必要がある。
-- ❌ エラー
SELECT deptno, SUM(sal) FROM emp;
-- ✅ 正しい
SELECT deptno, SUM(sal) FROM emp GROUP BY deptno;
動作確認
■テストデータ
DROP TABLE dept PURGE;
DROP TABLE emp PURGE;
-- 部門テーブル
CREATE TABLE dept (
deptno NUMBER(2) PRIMARY KEY,
dname VARCHAR2(14),
loc VARCHAR2(13)
);
-- 社員テーブル
CREATE TABLE emp (
empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4),
hiredate DATE,
sal NUMBER(7,2),
comm NUMBER(7,2),
deptno NUMBER(2) REFERENCES dept(deptno)
);
INSERT INTO dept (deptno, dname, loc) VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept (deptno, dname, loc) VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept (deptno, dname, loc) VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO dept (deptno, dname, loc) VALUES (40, 'OPERATIONS', 'BOSTON');
-- deptno = 10 (ACCOUNTING)
INSERT INTO emp VALUES (1001, 'SMITH', 'CLERK', 1100, DATE '2020-01-15', 1200, NULL, 10);
INSERT INTO emp VALUES (1002, 'ALLEN', 'SALESMAN', 1100, DATE '2019-03-10', 1600, 300, 10);
INSERT INTO emp VALUES (1003, 'WARD', 'SALESMAN', 1100, DATE '2018-07-20', 1250, 500, 10);
-- deptno = 20 (RESEARCH)
INSERT INTO emp VALUES (2001, 'JONES', 'MANAGER', 1100, DATE '2017-04-02', 2975, NULL, 20);
INSERT INTO emp VALUES (2002, 'SCOTT', 'ANALYST', 2001, DATE '2021-12-09', 3000, NULL, 20);
INSERT INTO emp VALUES (2003, 'ADAMS', 'CLERK', 2002, DATE '2022-01-12', 1100, NULL, 20);
INSERT INTO emp VALUES (2004, 'FORD', 'ANALYST', 2001, DATE '2019-06-25', 3000, NULL, 20);
-- deptno = 30 (SALES)
INSERT INTO emp VALUES (3001, 'MARTIN','SALESMAN', 1100, DATE '2020-09-28', 1250, 1400, 30);
INSERT INTO emp VALUES (3002, 'BLAKE', 'MANAGER', 1100, DATE '2015-05-01', 2850, NULL, 30);
INSERT INTO emp VALUES (3003, 'TURNER','SALESMAN', 3002, DATE '2019-09-08', 1500, 0, 30);
INSERT INTO emp VALUES (3004, 'JAMES', 'CLERK', 3002, DATE '2021-12-03', 950, NULL, 30);
-- deptno = 40 (OPERATIONS)
INSERT INTO emp VALUES (4001, 'MILLER','CLERK', 2001, DATE '2020-01-23', 1300, NULL, 40);
INSERT INTO emp VALUES (4002, 'CLARK', 'MANAGER', 1100, DATE '2016-06-09', 2450, NULL, 40);
COMMIT;
② AVG()
複数行の列値の平均を求める(数値型のみ対象)。
SELECT AVG(sal) FROM emp;
⚠️ 試験ポイント:
AVG()はNULLを無視して計算される。つまり、NULLの行は分子(合計)にも分母(行数)にも含まれない。
NVL()でNULLを0に変換してからAVGを使うと、分母にNULL行が含まれ、結果が変わることがある点に注意。
③ MAX()
複数行の列値の最大値を求める。
SELECT MAX(sal) FROM emp;
SELECT MAX(ename) FROM emp; -- 文字列も可
SELECT MAX(hiredate) FROM emp; -- 日付も可
- 数値型以外(文字列・日付型)にも使用可能
- NULLは無視される
④ MIN()
複数行の列値の最小値を求める。
SELECT MIN(sal) FROM emp;
- 数値型以外(文字列・日付型)にも使用可能
- NULLは無視される
⑤ COUNT()
⚠️ 番号ミスの修正:元の記事では「④COUNT」と記載されていたが、正しくは⑤番目。
行数を数えるファンクション。COUNT(*)とCOUNT(列名)で動作が異なる点が最重要ポイント。
| 書き方 | NULLの扱い | 説明 |
|---|---|---|
COUNT(*) |
NULLを含む | テーブルの全行数をカウント |
COUNT(列名) |
NULLを除外 | 指定列にNULL以外の値がある行数のみカウント |
COUNT(DISTINCT 列名) |
NULLを除外 | 重複・NULLを除いたユニーク値の件数 |
SELECT COUNT(*) FROM emp; -- 全行数(NULL含む)
SELECT COUNT(comm) FROM emp; -- commがNULLでない行数
SELECT COUNT(DISTINCT deptno) FROM emp; -- 重複なし件数
NULLの扱い
集計ファンクションにおけるNULLの基本ルール:
| 関数 | NULLの扱い |
|---|---|
| SUM / AVG | 無視(計算対象外) |
| MAX / MIN | 無視(NULL以外から最大・最小を探す) |
| COUNT(列名) | 無視(NULLの行はカウントしない) |
| COUNT(*) | 含む(NULLのある行も1行としてカウント) |
⚠️ AVGのNULL注意点:
NVL()等を使って事前にNULLを0に変換すると、分母(行数)が増えるため、平均値が変わる。どちらの挙動が求められているかを問題文で確認すること。
DISTINCTの使用
DISTINCTを集計ファンクション内に指定すると、重複データを除いてから集計が実行される。
SELECT COUNT(DISTINCT deptno) FROM emp;
SELECT SUM(DISTINCT sal) FROM emp;
SELECT AVG(DISTINCT sal) FROM emp;
動作確認
■テストデータ
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9001, 'A_SMITH', 'CLERK', NULL, DATE '2024-01-01', 1000, NULL, 10);
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9002, 'B_ALLEN', 'CLERK', NULL, DATE '2024-01-02', 1000, NULL, 10);
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9003, 'C_WARD', 'CLERK', NULL, DATE '2024-01-03', 2000, NULL, 20);
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9004, 'D_JONES', 'CLERK', NULL, DATE '2024-01-04', 3000, NULL, 30);
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9005, 'E_BLAKE', 'CLERK', NULL, DATE '2024-01-05', 3000, NULL, 30);
INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES
(9006, 'F_CLARK', 'CLERK', NULL, DATE '2024-01-06', NULL, NULL, 30);
COMMIT;
使用できる・できない構文:
| 構文 | 可否 | 理由 |
|---|---|---|
COUNT(DISTINCT 列名) |
✅ 使用可 | NULLを除いた重複なし件数 |
COUNT(DISTINCT *) |
❌ 使用不可 |
*にDISTINCTは指定できない(構文エラー) |
SUM(DISTINCT 列名) |
✅ 使用可 | 重複を除いた合計 |
AVG(DISTINCT 列名) |
✅ 使用可 | 重複を除いた平均 |
⚠️
COUNT(DISTINCT *)はエラーになる。DISTINCTと組み合わせる場合は必ず列名を指定する。
試験頻出ポイントまとめ
| ポイント | 内容 |
|---|---|
| WHERE句 | 集計ファンクションは使えない → HAVING句を使う |
| COUNT(*)とCOUNT(列名) | COUNT(*)はNULL含む、COUNT(列名)はNULL除外 |
| AVGのNULL | NULLは分子・分母ともに除外される(NVL変換で挙動が変わる) |
| MAX/MINの対象型 | 数値・文字列・日付すべてに対応 |
| COUNT(DISTINCT *) | 構文エラー(列名を指定する必要あり) |
| GROUP BYなしの複数列SELECT | 集計ファンクションと個別列の混在はエラー(ORA-00937) |
参考
Oracle SQL 集計関数のNULL対策とDISTINCTの使い方
https://oracle-master00000.com/null-distinct001/
Oracle Database SQL (1Z0-071-JPN)試験 NULLとDISTINCTの扱い(Qiita)
https://qiita.com/n-aaaa/items/3b4d3d66aeca9a6f19e9
SQLのHAVING句の使い方(各DB共通/Oracle含む解説)
https://cs-techblog.com/db/sql-having-of/
【SQL】HAVING句の使い方を1分でわかりやすく解説
https://it-biz.online/it-skills/having/
SQLのHAVING句とは?(OracleやMySQLで使用する方法)
https://it-kyujin.jp/article/detail/1755/
【SQL】DISTINCTとは?重複行をまとめる基本の使い方から簡単なサンプルまで
https://blastengine.jp/blog_content/sql-distinct/











