■ はじめに
https://dk521123.hatenablog.com/entry/2010/07/17/052306
でGROUP BY句を扱い、 『GROUP BY句の結果を条件絞り込みしたい場合はHAVING句』と 記載した。 そこで、今回は、SQL の HAVING句 について扱う
目次
【1】HAVING句 【2】構文 【3】サンプル 1)Hello world 2)重複データを取得する 3)グループ化してNULLとNOT NULLどちらも含まるデータを取得する 【4】使用上の注意 1)パフォーマンスについて 2)WHERE/GROUP BY/HAVINGの処理順に注意
【1】HAVING句
* WHERE句では、GROUP BY句の結果を条件絞り込むことができないため、 代わりに HAVING句を使う => WHERE句は、GROUP BY句よりも前に実行される => グループ分けしたレコードから、 さらに特定の条件に合致したレコードを引き出す際に使用
【2】構文
SELECT [カラム名] FROM [テーブル名] GROUP BY [集約キー] HAVING [条件]
【3】サンプル
1)Hello world
使い方イメージ
SELECT Sum(売上.売上額) AS 総売上額 , 商品.商品名 FROM 商品, 売上 GROUP BY 商品.商品ID HAVING SUM(売上.売上額) >= 6000000;
2)重複データを取得する
https://qiita.com/necoyama3/items/4c24defd6f504366aebe
SELECT 列A FROM テーブルA GROUP BY 列A HAVING COUNT(列A) > 1
3)グループ化してNULLとNOT NULLどちらも含まるデータを取得する
* PostgreSQL17を想定
使用データ
CREATE TABLE purchase_history( user_id INT, purchase_id INT, updated_date DATE ); INSERT INTO purchase_history(user_id, purchase_id, updated_date) VALUES (1,1,'2024-09-14'), -- ★これが欲しい (1,NULL,'2024-09-15'), -- ★これが欲しい (2,NULL,'2024-09-14'), (3,2, '2024-09-14'), (4,3,'2024-09-14'), -- ★これが欲しい (4,NULL,'2024-09-16'), -- ★これが欲しい (5,4, '2024-09-14'), (5,5, '2024-09-15') ;
SELECT * FROM purchase_history WHERE user_id IN ( SELECT user_id FROM purchase_history GROUP BY user_id HAVING COUNT(CASE WHEN purchase_id IS NULL THEN 1 END) > 0 AND COUNT(CASE WHEN purchase_id IS NOT NULL THEN 1 END) > 0 );
user_id | purchase_id | updated_date |
---|---|---|
1 | 1 | 2024-09-14 |
1 | 2024-09-15 | |
4 | 3 | 2024-09-14 |
4 | 2024-09-16 |
【4】使用上の注意
1)パフォーマンスについて
* パフォーマンスがよろしくないので、極力、WHERE文に任せる => 詳細は、以下の記事の「HAVINGの使用は、極力避ける」を参照のこと
https://dk521123.hatenablog.com/entry/2012/05/05/003504
2)WHERE/GROUP BY/HAVINGの処理順に注意
* HAVING句には集約後にしか判定できない条件を記述するようにする => SELECT -> FROM -> WHERE ->GROUP BY -> HAVING -> ORDER BY の順番で書く(この順に処理される)
補足説明
* DBは、WHERE/GROUP BY/HAVINGを以下の順序で処理する
処理順序
[1] WHEREの条件に合致した行を抽出する <= ここで出来る限りデータ行を少なくした方がいい [2] GROUP BYに従いグループ分けして集約する [3] HAVINGの条件に合致した集約行を抽出する => WHEREには集約前に判定できる条件を全て記述し、 HAVINGには集約後にしか判定できない条件 (下記のような平均点??以上とか)を記述する
例:合格者で平均90点以上の受験者
SELECT c.ID, c.Name, e.Score FROM Candidate c INNER JOIN Exam e ON c.ID = e.candidateID WHERE c.Grade = 'Pass' GROUP BY e.ID HAVING avg(e.Score) > 90 ;
参考文献
http://itpro.nikkeibp.co.jp/article/lecture/20061211/256508/?ST=selfup&P=2
関連記事
SQL ~ GROUP BY句 ~
https://dk521123.hatenablog.com/entry/2010/07/17/052306
SQL ~ IN句 ~
https://dk521123.hatenablog.com/entry/2024/12/01/000000
パフォーマンスの良いSQLを記述 ~検索条件編~
https://dk521123.hatenablog.com/entry/2012/05/05/003504
複合キーの重複データを取得することを考える
https://dk521123.hatenablog.com/entry/2022/06/06/125440