ざっくりSQL入門 #6:クエリを書く<サブクエリ&UNION編>
※MySQLを前提に書いています。一部関数などは方言差がある場合もあるのでご注意ください。
今回は、サブクエリやUNIONを用いて、複数のクエリの結果を組み合わせる方法について。
サブクエリとUNION、その違いについて
サブクエリは「あるSELECTクエリをテーブルや値とみなして扱う」方法、
UNIONは「複数のSELECTクエリの結果セットを足し合わせる」方法です。
サブクエリ
前述したとおり、「あるSELECTクエリをテーブルや値とみなして扱う」方法です。
テーブルとみなす
以下のように、
FROM句やJOIN句の部分にテーブル名ではなく「カッコで括ったSELECTクエリ」を用いることで
そのクエリの結果セットをテーブルとみなして扱うことができます。
SELECT ... FROM ( SELECT ... FROM ... WHERE ... ) AS `table_sub` WHERE ...
例えば以下は、
「商品の注文テーブルを用いて売上個数と金額を集計したものに商品マスタをJOINして、集計結果を求めるクエリ」
です。
SELECT `summary`.`product_id` -- 商品ID ,`product`.`name` -- 商品名 ,`summary`.`count_sum` -- 売上個数の合計 ,`summary`.`price_sum` -- 売上金額の合計 FROM ( -- product_idごとに、count(売上個数)とprice(売上金額)のSUMを取る SELECT `product_id`, SUM(`count`) AS `count_sum`, SUM(`price`) AS `price_sum` FROM `order_product` GROUP BY `product_id` ) AS `summary` LEFT JOIN `product` ON `summary`.`product_id` = `product`.`product_id`
値とみなす
以下のように、ひとつもしくは複数の値とみなすこともできます。
-- 複数の値とみなす:サブクエリ内のカラムが1つのみである必要があります SELECT ... FROM ... WHERE `hoge` IN (SELECT `fuga` FROM ... WHERE ...) -- ひとつの値とみなす:サブクエリ内のカラムが1つのみ、かつレコードが1件のみである必要があります SELECT ... FROM ... WHERE `hoge` = (SELECT `fuga` FROM ... WHERE ...)
例えば以下は
「2018年5月1日以降に売れた商品の一覧を求めるクエリ」
です。
(これは単純な例なので、JOINでも簡単に置き換えることができますが…)
SELECT `product`.`product_id` ,`product`.`name` FROM `product` WHERE `product`.`product_id` IN (SELECT DISTINCT `product_id` FROM `order_product` WHERE `order_date` >= '2018-05-01 00:00:00')
UNION
前述したとおり、「複数のSELECTクエリの結果セットを足し合わせる」方法です。
こちらは実現したいこととしては単純で、例えば
SELECT `id`, `name` FROM `user_a` WHERE ...
で得られる
id | name |
1 | Alex |
2 | Bill |
3 | Cathy |
という結果セットと
SELECT `id`, `name` FROM `user_b` WHERE ...
で得られる
id | name |
4 | Daisy |
5 | Emily |
6 | Fir |
という結果セットを足し合わせ
id | name |
1 | Alex |
2 | Bill |
3 | Cathy |
4 | Daisy |
5 | Emily |
6 | Fir |
という結果セットを(一度のクエリで)得たい場合に使用します。
書き方も単純で、
(SELECT `id`, `name` FROM `user_a` WHERE ...) UNION (SELECT `id`, `name` FROM `user_b` WHERE ...)
と、二つのクエリを「UNION」でつなぐだけです。
二つのクエリはカッコで括らなくても動作しますが、
明示的に括ってしまったほうが可読性は上がるかと思います。
(後述の注意点も参照のこと)
注意点
デフォルトでは、足し合わせた結果セット内に重複レコードが発生する場合、除かれます。
重複していても削除したくない場合は「UNION ALL」とすればOKです。
また、ORDER句の取り扱いにはやや注意が必要です。
-- ORDER/LIMITで絞り込んだクエリ同士をUNIONする -- (このパターンでは、カッコを外して書くとエラーになります!) (SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT 5) UNION (SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT 5) -- UNIONした後のクエリをORDER/LIMITで絞り込む (SELECT ... FROM ... WHERE ...) UNION (SELECT ... FROM ... WHERE ...) ORDER BY ... LIMIT 10
詳細は公式マニュアルを参照してください。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.9.4 UNION 構文