🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Q&A

解決済

3回答

1188閲覧

「GROUP BY」と「HAVING」のどちらを使えばいいのか?

ThouShaltNot

総合スコア3

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

0グッド

1クリップ

投稿2021/02/24 17:31

編集2021/02/24 17:50

MySQL5.7使用です。
GROUP_CONCATを書くとWHEREで存在しない条件なのに、レコードがNULLで取得されてしまう仕様を知りました。

解決策としてGROUP BYHAVINGがあるようですが、どちらを使えばいいのでしょうか?
またその理由を教えて頂けませんでしょうか?

今回は以下の例としての質問ですが、「他にこういう例ならこっちがいい」なども知りたいです。

テーブル例

MySQL

1CREATE TABLE persons ( 2 ID INT AUTO_INCREMENT PRIMARY KEY 3 ,age INT 4 ); 5 6INSERT INTO persons(age) values (50),(20),(71),(33),(18); 7 8CREATE TABLE children ( 9 person_ID INT 10 ,person_ID2 INT 11 ); 12 13INSERT INTO children(person_ID,person_ID2) values (1,2),(1,5),(3,4);

GROUP BY による方法

MySQL

1SELECT p.ID 2 ,GROUP_CONCAT(c.person_ID2) AS children_ids 3 4FROM persons p 5 LEFT JOIN children c ON c.person_ID = p.ID 6 7WHERE p.age > 100 8 9GROUP BY p.ID

HAVING による方法

MySQL

1SELECT p.ID 2 ,GROUP_CONCAT(c.person_ID2) AS children_ids 3 4FROM persons p 5 LEFT JOIN children c ON c.person_ID = p.ID 6 7WHERE p.age > 100 8 9HAVING children_ids IS NOT NULL

###追記
HAVINGではWHEREが存在するときの取得がおかしかったです。
次のようにWHERE p.age > 1とすると、ID=1 の子供は 2,5 だけなのに、2,5,4 が取得できてしまいました。

MySQL

1SELECT p.ID 2 ,GROUP_CONCAT(c.person_ID2) AS children_ids 3 4FROM persons p 5 LEFT JOIN children c ON c.person_ID = p.ID 6 7WHERE p.age > 1 8 9HAVING children_ids IS NOT NULL 10

そうなると新たな疑問です。
こちらの質問で、ランキング上位の方がGROUP BYではなくHAVINGを提案されているのですが、これは間違ったご回答になるのでしょうか?

しかし的確なご回答ばかりされている方なので、とても間違いだとは思えません。
HAVINGが良い理由を知りたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2021/02/24 21:48

テーブル定義とデータと取得したいデータ(要件)によると思います
macof

2021/02/24 23:53

元の質問は単に集約キーを指定せず集約関数を使用した際 0レコードなら空集合になって欲しいと言う要件でしか無いので、根本的に話が異なると思いますが… 因みに追記されたSQLにおけるIDカラムは集約キーに指定されていないため、偶然1になっているに過ぎません。
guest

回答3

0

前提としてgroup byhavingどちらと比較するものではないのでSQLの理解を誤っています

基本的にGROUP BY (集約) 関数
を使う場合、集約対象外の列をgroup byしなくてはいけません

sql

1mysql> SELECT p.ID 2 -> ,GROUP_CONCAT(c.person_ID2) AS children_ids 3 -> FROM persons p 4 -> LEFT JOIN children c ON c.person_ID = p.ID 5 -> GROUP BY p.ID 6 -> ; 7+----+--------------+ 8| ID | children_ids | 9+----+--------------+ 10| 1 | 5,2 | 11| 2 | NULL | 12| 3 | 4 | 13| 4 | NULL | 14| 5 | NULL | 15+----+--------------+

havinggroup byした列を対象に条件指定する時に使うもので、上のSQLのgroup byの後に付ける事で例えば不要な行(null行など)を除外する事ができます

sql

1having children_ids is not null; 2+----+--------------+ 3| ID | children_ids | 4+----+--------------+ 5| 1 | 5,2 | 6| 3 | 4 | 7+----+--------------+

whereは上記関数の当てる対象行を絞る為に使います
具体的には上のSQLのgroup byの前にwhereを追加して使います

sql

1where c.person_ID2>2 2+----+--------------+ 3| ID | children_ids | 4+----+--------------+ 5| 1 | 5 | 6| 3 | 4 | 7+----+--------------+

基本的にと書いたわけ

mysqlがまさに例外で、group byがなくても動いてしまう これは自分で調べてください

投稿2021/02/25 00:17

hentaiman

総合スコア6426

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ThouShaltNot

2021/02/25 03:26

それぞれの使い方が理解できました。ありがとうございます。
guest

0

ベストアンサー

havinggroup byの過程で実施されます。
GROUP_CONCATも同様にgroup byの過程での実施です。

ですので、GROUP_CONCATの結果がNullの場合を除外するには、havingである必要があります。

ただ、誤解されているようなので補足しておくと、GROUP_CONCATの過程でのNullは除外されます。
1,2,3,Nullのような結果は返却されません。
すべてがNullだった場合だけNullが返却されるのです。

HAVINGではWHEREが存在するときの取得がおかしかったです。
次のようにWHERE p.age > 1とすると、ID=1 の子供は 2,5 だけなのに、2,5,4 が取得できてしまいました。

データ作成時にIDの値を指定していないから、意図しない値になっているという事はありませんか?
実際のデータを確認してみて下さい。

それから、質問のSQLではGROUP_CONCATは全体に対して行われています。
ID毎に行う場合は、GROUP BY IDが必要です。

投稿2021/02/25 00:16

編集2021/02/25 00:27
sazi

総合スコア25327

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ThouShaltNot

2021/02/25 03:32

なるほどです。後半の「データ作成時にIDの値を指定していないから、意図しない値になっている」というのはどういう意味でしょうか?以下、11行目にIDのカラムを追加しましたが、 http://sqlfiddle.com/#!9/dad283/1 やはり「ID=1 の子供は 2,5 だけなのに、2,5,4 が取得できてしまいました。」の結果は変わりませんでした。
ThouShaltNot

2021/02/25 03:42

「データ作成時にIDの値を指定していないから、意図しない値になっている」というよりも、「データ取得時にIDの値で集約していないから、意図しない値になっている」と仰りたかったのではないでしょうか? 以下、IDの値で集約すれば、意図した値になります。 http://sqlfiddle.com/#!9/2a622a/1
sazi

2021/02/25 04:21 編集

children のperson_IDの値はpersons のIDがそうなる前提ですけど、ID はAUTO_INCREMENTになっていますので、データをdeleteして再度insertした場合には、1から始まる訳では無いので、その可能性が無いかという意味です。 こういった値を指定する場合はpersons へのinsert時にIDの値を明示するべきです。
sazi

2021/02/25 04:29 編集

質問のSQLは手段なので、目的(やりたいこと)と一致しているかどうかは分からないのです。 > ID=1 の子供は 2,5 だけなのに、2,5,4 が取得できてしまいました。 という事から察するに、先ずは、GROUP BY IDが不足しているという事だと思いますが、 そもそもは、group_concat()は集計関数であるという事、またはGROUP BYに関する理解が不足しています。
ThouShaltNot

2021/02/25 06:55

詳しくありがとうございました。
guest

0

HAVINGはGROUP BYしないと

SQL

1SELECT p.ID 2,GROUP_CONCAT(c.person_ID2) AS children_ids 3FROM persons as p 4LEFT JOIN children c ON c.person_ID = p.ID 5AND p.age>100 6GROUP BY p.ID 7HAVING children_ids IS NOT NULL

投稿2021/02/25 00:19

yambejp

総合スコア116688

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ThouShaltNot

2021/02/25 03:24

ソースコードから意図が測る力量がなく、仰りたいことがわかりませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問