集約関数の OVER(合計や平均を行ごとに配る)の使い方
shain社員
| busho_id |
|---|
| 1 |
| 2 |
| 2 |
| 2 |
| 2 |
| 3 |
| … |
部署の合計を足す合計を配った結果
| busho_id | busho_kei |
|---|---|
| 1 | 980000 |
| 2 | 2215050 |
| 2 | 2215050 |
| 2 | 2215050 |
| 2 | 2215050 |
| 3 | 2131000 |
| … | |
sum(salary) OVER (PARTITION BY busho_id) AS busho_kei
- 青い列= 式から作った列。元の表には無い列です
- 社員 18 行 → 合計を配った結果 18 行(行数は変わりません。減らすのは WHERE の仕事です)
同じ部署の行には同じ値が入ります。部署 2 の 4 行はどれも 2215050 です。合計を出しても行はまとまらず、1 人 1 行のままであることに注目してください。
集約関数に OVER を付けると、行を潰さずに集計結果を各行へ配れます。 GROUP BY は 18 行を 7 行にまとめてしまいますが、OVER なら 18 行のまま「自分の部署の合計」を横に置けるので、個々の値と全体をひとつの表で見比べられます。
未確認の製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database
未確認: IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01行を潰さずに集計する
結論: sum(salary) は行をまとめますが、sum(salary) OVER (…) はまとめません。
GROUP BY で部署ごとの合計を出すと、部署の数だけの行になります。合計は分かりますが、誰がいくらもらっているかは消えます。
OVER を付けると、同じ合計を元の行それぞれの横に置きます。行数は変わりません。「自分の給与」と「自分の部署の合計」を並べて見たいときは、こちらです。
⚠️ OVER を付けたものはウィンドウ関数と呼び、GROUP BY とは別の仕組みです。同じ sum でも、OVER があるかどうかで結果の行数がまったく変わります。
SELECT busho_id, name, salary,元の列はそのまま出せるsum(salary)足すものOVER (PARTITION BY busho_id)★どこまでを足すか(同じ部署の行)AS busho_kei作った列に名前を付けるFROM shain行は 1 つも減らないORDER BY busho_id, shain_id;結果の並べ方
GROUP BY が「行をまとめる指示」なのに対し、PARTITION BY は「どの行までを足すか」を決めるだけです。行はまとまりません。
SELECT busho_id,
count(*) AS ninzu,
sum(salary) AS goukei
FROM shain
GROUP BY busho_id
ORDER BY busho_id;| busho_id | ninzu | goukei |
|---|---|---|
| 1 | 1 | 980000 |
| 2 | 4 | 2215050 |
| 3 | 4 | 2131000 |
| 4 | 3 | 1617000 |
| 5 | 2 | 1078000 |
| 7 | 2 | 903000 |
| NULL | 2 | 832000 |
(7 行)
7 行になります。部署ごとの合計は分かりますが、社員 1 人 1 人は消えてしまいました。
SELECT busho_id,
name,
salary,
sum(salary) OVER (PARTITION BY busho_id) AS busho_kei
FROM shain
ORDER BY busho_id, shain_id;| busho_id | name | salary | busho_kei |
|---|---|---|---|
| 1 | 鈴木 | 980000 | 980000 |
| 2 | 佐藤 | 720000 | 2215050 |
| 2 | 田中 | 520000 | 2215050 |
| 2 | 中村 | 530000 | 2215050 |
| 2 | 小林 | 445050 | 2215050 |
| 3 | 山田 | 700000 | 2131000 |
| 3 | 高橋 | 480000 | 2131000 |
| 3 | 伊藤 | 530000 | 2131000 |
| 3 | 松本 | 421000 | 2131000 |
| 4 | 清水 | 650000 | 1617000 |
| 4 | 斎藤 | 455000 | 1617000 |
| 4 | 山本 | 512000 | 1617000 |
| 5 | 加藤 | 610000 | 1078000 |
| 5 | 吉田 | 468000 | 1078000 |
| 7 | 井上 | 505000 | 903000 |
| 7 | 木村 | 398000 | 903000 |
| NULL | 渡辺 | 400000 | 832000 |
| NULL | 大野 | 432000 | 832000 |
(18 行)
18 行のままです。合計の値は GROUP BY のときとまったく同じで、置き場所だけが違います。
02PARTITION BY を省くと全体が対象になる
結論: OVER () と空にすると、まとまりは「全行」になります。
PARTITION BY はどこで区切るかを決めるものです。書かなければ区切りが無い、つまり表全体がひとつのまとまりです。
両方を並べると、区切りの有無が値の違いとしてそのまま出ます。全体の合計は全行で同じ値になり、部署の合計は部署が変わると変わります。
SELECT name,
salary,
sum(salary) OVER () AS zentai_kei,
sum(salary) OVER (PARTITION BY busho_id) AS busho_kei
FROM shain
ORDER BY shain_id;| name | salary | zentai_kei | busho_kei |
|---|---|---|---|
| 鈴木 | 980000 | 9756050 | 980000 |
| 佐藤 | 720000 | 9756050 | 2215050 |
| 田中 | 520000 | 9756050 | 2215050 |
| 中村 | 530000 | 9756050 | 2215050 |
| 小林 | 445050 | 9756050 | 2215050 |
| 加藤 | 610000 | 9756050 | 1078000 |
| 吉田 | 468000 | 9756050 | 1078000 |
| 山田 | 700000 | 9756050 | 2131000 |
| 高橋 | 480000 | 9756050 | 2131000 |
| 伊藤 | 530000 | 9756050 | 2131000 |
| 松本 | 421000 | 9756050 | 2131000 |
| 井上 | 505000 | 9756050 | 903000 |
| 木村 | 398000 | 9756050 | 903000 |
| 清水 | 650000 | 9756050 | 1617000 |
| 斎藤 | 455000 | 9756050 | 1617000 |
| 山本 | 512000 | 9756050 | 1617000 |
| 渡辺 | 400000 | 9756050 | 832000 |
| 大野 | 432000 | 9756050 | 832000 |
(18 行)
全体の合計 9756050 は全行で同じ値です。部署の合計は部署が変わると変わります。開発の 4 人(佐藤・田中・中村・小林)だけが同じ 2215050 になっています。
03自分がまとまりの何割かを出す
結論: 元の値をまとまりの合計で割れば、その行が占める割合になります。
これは OVER がいちばん効く使い方です。GROUP BY では合計を出した時点で個々の値が消えているので、割り算の相手が同じ行に無いからです。OVER なら両方が同じ行に並ぶので、そのまま割れます。
⚠️ 整数どうしの割り算は整数に切り捨てられる製品があります。100.0 のように小数を混ぜてから割ってください。
SELECT busho_id,
name,
salary,
sum(salary) OVER (PARTITION BY busho_id) AS busho_kei,
round(100.0 * salary / sum(salary) OVER (PARTITION BY busho_id), 1) AS wariai
FROM shain
WHERE busho_id IN (2, 5)
ORDER BY busho_id, shain_id;| busho_id | name | salary | busho_kei | wariai |
|---|---|---|---|---|
| 2 | 佐藤 | 720000 | 2215050 | 32.5 |
| 2 | 田中 | 520000 | 2215050 | 23.5 |
| 2 | 中村 | 530000 | 2215050 | 23.9 |
| 2 | 小林 | 445050 | 2215050 | 20.1 |
| 5 | 加藤 | 610000 | 1078000 | 56.6 |
| 5 | 吉田 | 468000 | 1078000 | 43.4 |
(6 行)
部署 5 は 2 人しかいないので、2 人の割合を足すと 100.0 になります。部署 2 は 4 人なので 1 人あたりの割合が小さくなります。
04ORDER BY を足すと「その行まで」になる
結論: OVER の中に ORDER BY を書くと、合計の範囲が「先頭からその行まで」に変わります。 これが累計です。
ORDER BY が無いときはまとまり全体を足していました。ORDER BY を足すと、その並びで自分より前にある行と自分だけが足されます。
同じ sum でも、OVER の中身ひとつで「全体の合計」にも「累計」にもなります。最後の行の累計は、必ず全体の合計と一致します。
SELECT shain_id,
name,
salary,
sum(salary) OVER (ORDER BY shain_id) AS ruikei
FROM shain
ORDER BY shain_id;| shain_id | name | salary | ruikei |
|---|---|---|---|
| 1 | 鈴木 | 980000 | 980000 |
| 2 | 佐藤 | 720000 | 1700000 |
| 3 | 田中 | 520000 | 2220000 |
| 4 | 中村 | 530000 | 2750000 |
| 5 | 小林 | 445050 | 3195050 |
| 6 | 加藤 | 610000 | 3805050 |
| 7 | 吉田 | 468000 | 4273050 |
| 8 | 山田 | 700000 | 4973050 |
| 9 | 高橋 | 480000 | 5453050 |
| 10 | 伊藤 | 530000 | 5983050 |
| 11 | 松本 | 421000 | 6404050 |
| 12 | 井上 | 505000 | 6909050 |
| 13 | 木村 | 398000 | 7307050 |
| 14 | 清水 | 650000 | 7957050 |
| 15 | 斎藤 | 455000 | 8412050 |
| 16 | 山本 | 512000 | 8924050 |
| 17 | 渡辺 | 400000 | 9324050 |
| 18 | 大野 | 432000 | 9756050 |
(18 行)
1 行目は自分の給与そのもの、2 行目は 1 行目との合計、と増えていきます。最後の大野の 9756050 は、前の節で出た全体の合計と同じ値です。
05⚠️ よくある間違い:同じ値の行はまとめて足される
結論: 累計の並び順に同じ値の行があると、その行たちは「まとめて」足されます。 1 行ずつ増えません。
給与の高い順で累計を取ると、給与が同額の中村と伊藤はどちらも同じ累計値になります。片方だけ足した途中の値は出てきません。
これは既定の集計範囲が「同じ並び順の値を持つ行までを一括で含む」ものだからです。1 行ずつ積み上げたい場合の書き方(ROWS 指定)は製品によって対応が分かれるため、このページでは扱いません。
まずは「並びに同じ値があると段が飛ぶ」ことを知っておいてください。 累計が思った増え方をしないときは、ここを疑います。決着が付く列を並びに足せば、1 行ずつ増えるようになります。
SELECT name,
salary,
sum(salary) OVER (ORDER BY salary DESC) AS ruikei
FROM shain
WHERE taishoku_on IS NULL
ORDER BY salary DESC, shain_id;| name | salary | ruikei |
|---|---|---|
| 鈴木 | 980000 | 980000 |
| 佐藤 | 720000 | 1700000 |
| 山田 | 700000 | 2400000 |
| 清水 | 650000 | 3050000 |
| 加藤 | 610000 | 3660000 |
| 中村 | 530000 | 4720000 |
| 伊藤 | 530000 | 4720000 |
| 田中 | 520000 | 5240000 |
| 井上 | 505000 | 5745000 |
| 吉田 | 468000 | 6213000 |
| 斎藤 | 455000 | 6668000 |
| 小林 | 445050 | 7113050 |
| 大野 | 432000 | 7545050 |
| 松本 | 421000 | 7966050 |
| 渡辺 | 400000 | 8366050 |
| 木村 | 398000 | 8764050 |
(16 行)
中村と伊藤はどちらも 4720000 です。1 つ前の加藤の 3660000 から、530000 が 2 人ぶん一気に足されています。
SELECT name,
salary,
sum(salary) OVER (ORDER BY salary DESC, shain_id) AS ruikei
FROM shain
WHERE taishoku_on IS NULL
ORDER BY salary DESC, shain_id;| name | salary | ruikei |
|---|---|---|
| 鈴木 | 980000 | 980000 |
| 佐藤 | 720000 | 1700000 |
| 山田 | 700000 | 2400000 |
| 清水 | 650000 | 3050000 |
| 加藤 | 610000 | 3660000 |
| 中村 | 530000 | 4190000 |
| 伊藤 | 530000 | 4720000 |
| 田中 | 520000 | 5240000 |
| 井上 | 505000 | 5745000 |
| 吉田 | 468000 | 6213000 |
| 斎藤 | 455000 | 6668000 |
| 小林 | 445050 | 7113050 |
| 大野 | 432000 | 7545050 |
| 松本 | 421000 | 7966050 |
| 渡辺 | 400000 | 8366050 |
| 木村 | 398000 | 8764050 |
(16 行)
中村が 4190000、伊藤が 4720000 と別々になりました。社員番号を並びに足したことで、2 人の間に前後が付いたためです。
06⚠️ よくある間違い:WHERE と HAVING では使えない
結論: WHERE にも HAVING にもウィンドウ関数は書けません。エラーになります。
どちらもウィンドウ関数より先に評価されるためです。行を絞る時点では、まだ合計は配られていません。
「部署の合計が 200 万円を超える部署の社員だけ」のような条件は、いったん合計を配った結果を表として作ってから、その外側で絞り込みます。
書き方は FROM の中に副問い合わせを置くか、WITH で名前を付けるかの 2 通りです。どちらでも結果は同じです。
SELECT name, salary
FROM shain
WHERE sum(salary) OVER (PARTITION BY busho_id) > 2000000;「window functions are not allowed in WHERE」と出ます。書き方の間違いではなく、評価される順番の問題です。
SELECT busho_id, name, salary, busho_kei
FROM (SELECT busho_id,
name,
salary,
sum(salary) OVER (PARTITION BY busho_id) AS busho_kei
FROM shain) t
WHERE busho_kei > 2000000
ORDER BY busho_id, salary DESC;| busho_id | name | salary | busho_kei |
|---|---|---|---|
| 2 | 佐藤 | 720000 | 2215050 |
| 2 | 中村 | 530000 | 2215050 |
| 2 | 田中 | 520000 | 2215050 |
| 2 | 小林 | 445050 | 2215050 |
| 3 | 山田 | 700000 | 2131000 |
| 3 | 伊藤 | 530000 | 2131000 |
| 3 | 高橋 | 480000 | 2131000 |
| 3 | 松本 | 421000 | 2131000 |
(8 行)
8 行返ります。部署の合計が 200 万円を超えているのは開発(2215050)と営業(2131000)の 2 部署で、その所属者だけが残っています。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
ここで出てくる関数を引く
関連するトピック
- ROW_NUMBER / RANK / DENSE_RANK同じ OVER を順位付けに使う書き方です。
- GROUP BY 句行をまとめるほうの集計はこちらです。
- 集約関数SUM や COUNT そのものの説明はこちらです。
- HAVING 句まとめた結果を絞り込む書き方です。
- FROM 句の副問い合わせ配った結果を絞り込むときに使う書き方です。
根拠(一次情報)
- PostgreSQL 18 マニュアル: ウィンドウ関数(集約関数の OVER)一次情報・確認 2026-08-11
- PostgreSQL 18 マニュアル: ウィンドウ関数の呼び出しと集計範囲一次情報・確認 2026-08-11