【SQL】SQL ~ HAVING句 ~

■ はじめに

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を想定

https://www.db-fiddle.com/

使用データ

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')
;

SQL

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