inner join で条件を指定しないとどうなるか
inner join句は結合するテーブル同士でどのキーで結合するかを指定する。
指定しなくてもクエリは実行される(実行エラーにならない)が、挙動が変わる。
テスト用テーブル
customers (顧客テーブル)
id |
customer_name |
1 |
hirata |
2 |
kanazawa |
3 |
fukuda |
products (商品テーブル 名称と金額のみ)
id |
product_name |
price |
1 |
pencil |
100 |
2 |
eraser |
120 |
3 |
note |
150 |
order_history (注文履歴、注文数などは省略)
id |
customer_id |
product_id |
date |
1 |
1 |
1 |
2021-06-07 |
2 |
2 |
1 |
2021-06-07 |
3 |
2 |
2 |
2021-06-07 |
4 |
2 |
3 |
2021-06-08 |
5 |
3 |
1 |
2021-06-05 |
6 |
3 |
3 |
2021-06-08 |
基本的な(left) inner join句の書き方
-- 注文履歴と商品テーブルと顧客テーブルを結合
select * from order_history o inner join products p on o.product_id = p.id
result
o.id |
o.customer_id |
o.product_id |
o.order_date |
p.id |
p.product_name |
p.price |
c.id |
c.customer_name |
1 |
1 |
1 |
2021-06-07 |
1 |
pencil |
100 |
1 |
hirata |
2 |
2 |
1 |
2021-06-07 |
1 |
pencil |
100 |
2 |
kanazawa |
3 |
2 |
2 |
2021-06-07 |
2 |
eraser |
120 |
2 |
kanazawa |
4 |
2 |
3 |
2021-06-08 |
3 |
note |
150 |
2 |
kanazawa |
5 |
3 |
1 |
2021-06-05 |
1 |
pencil |
100 |
3 |
fukuda |
6 |
3 |
3 |
2021-06-08 |
3 |
note |
150 |
3 |
fukuda |
注文履歴をベースに、
結合条件を指定しない場合
select * from order_history o inner join products p inner join customers c;
result
o.id |
o.customer_id |
o.product_id |
o.order_date |
p.id |
p.product_name |
p.price |
c.id |
c.customer_name |
1 |
1 |
1 |
2021-06-07 |
1 |
pencil |
100 |
1 |
hirata |
1 |
1 |
1 |
2021-06-07 |
2 |
eraser |
120 |
1 |
hirata |
1 |
1 |
1 |
2021-06-07 |
3 |
note |
150 |
1 |
hirata |
1 |
1 |
1 |
2021-06-07 |
1 |
pencil |
100 |
2 |
kanazawa |
1 |
1 |
1 |
2021-06-07 |
2 |
eraser |
120 |
2 |
kanazawa |
1 |
1 |
1 |
2021-06-07 |
3 |
note |
150 |
2 |
kanazawa |
1 |
1 |
1 |
2021-06-07 |
1 |
pencil |
100 |
3 |
fukuda |
1 |
1 |
1 |
2021-06-07 |
2 |
eraser |
120 |
3 |
fukuda |
1 |
1 |
1 |
2021-06-07 |
3 |
note |
150 |
3 |
fukuda |
2 |
2 |
1 |
2021-06-07 |
1 |
pencil |
100 |
1 |
hirata |
2 |
2 |
1 |
2021-06-07 |
2 |
eraser |
120 |
1 |
hirata |
2 |
2 |
1 |
2021-06-07 |
3 |
note |
150 |
1 |
hirata |
2 |
2 |
1 |
2021-06-07 |
1 |
pencil |
100 |
2 |
kanazawa |
2 |
2 |
1 |
2021-06-07 |
2 |
eraser |
120 |
2 |
kanazawa |
....... |
|
|
|
|
|
|
|
|
と、結果が大量になるので途中までだが、結合条件を指定しないと全行で結合してしまう。
これはcross join指定をしているのと同じ。
select * from order_history o cross join products p cross join customers c
この挙動が便利な時(cross join)
全組み合わせが確認できるので、それなりに便利。
私は、日付ごとに集計結果を出力したい時で、「集計結果にならない日付も出力したい場合」に全ての日付でテーブルを作成する時に使用したりします。
例のorder_historyでは、6月5日、7日、8日のデータがあるため、これで商品の日付ごとを集計すると6日のデータが出力されない。
select product_id ,order_date, sum(price) from order_history o inner join products p on p.id = o.product_id group by product_id ,order_date;
product_id |
order_date |
sum(price) |
1 |
2021-06-05 |
100 |
1 |
2021-06-07 |
200 |
2 |
2021-06-07 |
120 |
3 |
2021-06-08 |
300 |
cross joinで商品✕日付の全組み合わせを出力しておいて、それに結果を紐付けると結果の無い日付も出力対象になる。
select p.id, p.product_name, d, order_date, sum_price from products p cross join
(select
date_format(date_add('2021-06-04', interval td.generate_series day), '%Y-%m-%d') as d
from
(
SELECT 0 generate_series FROM DUAL WHERE (@num:=1-1)*0 UNION ALL
SELECT @num:=@num+1 FROM `information_schema`.COLUMNS LIMIT 7
) as td
) date_table
left join
(select product_id ,order_date, sum(price) as sum_price from order_history o inner join products p on p.id = o.product_id group by product_id ,order_date) ledger
on ledger.product_id = p.id and ledger.order_date = date_table.d
order by p.id, d
;
id |
product_name |
d |
order_date |
sum_price |
1 |
pencil |
2021-06-05 |
2021-06-05 |
100 |
1 |
pencil |
2021-06-06 |
|
|
1 |
pencil |
2021-06-07 |
2021-06-07 |
200 |
1 |
pencil |
2021-06-08 |
|
|
1 |
pencil |
2021-06-09 |
|
|
1 |
pencil |
2021-06-10 |
|
|
1 |
pencil |
2021-06-11 |
|
|
2 |
eraser |
2021-06-05 |
|
|
2 |
eraser |
2021-06-06 |
|
|
2 |
eraser |
2021-06-07 |
2021-06-07 |
120 |
2 |
eraser |
2021-06-08 |
|
|
2 |
eraser |
2021-06-09 |
|
|
2 |
eraser |
2021-06-10 |
|
|
2 |
eraser |
2021-06-11 |
|
|
3 |
note |
2021-06-05 |
|
|
3 |
note |
2021-06-06 |
|
|
3 |
note |
2021-06-07 |
|
|
3 |
note |
2021-06-08 |
2021-06-08 |
300 |
3 |
note |
2021-06-09 |
|
|
3 |
note |
2021-06-10 |
|
|
3 |
note |
2021-06-11 |
|
|
MySQL8.0はWindow関数も使えたり集計関数も増えているので、5.7までで無理やりやるとこんな感じというところはありますが。
ともあれ、たまにSQL書こうとすると間違ったりするので注意。