Open3
達人に学ぶSQL徹底指南書

1,2,7は終わったので3からメモ
3 自己結合
結合だと紐づくレコードを取ってくるだけの使い方しかしたことなかったが、
値段が同じ商品を値段をキーとして取る、といった使い方ができて便利
Data
name | price |
---|---|
りんご | 50 |
みかん | 100 |
みかん | 100 |
みかん | 100 |
バナナ | 80 |
select distinct p1.name, p1.price
from Products p1
inner join Products p2
on p1.price = p2.price
where p1.name != p2.name

4 3論理値とNULL
NULL
は値ではなく、「値を決めることができない」という意味
値ではないので比較した時にTRUE/FALSE
とならない
極値関数を使用する場合、集合が空の場合NULL
を返すので注意
- Class_A
name | age | city |
---|---|---|
ブラウン | 22 | 東京 |
ボギー | 21 | 千葉 |
ラリー | 19 | 埼玉 |
- Class_B
name | age | city |
---|---|---|
山田 | 東京 | |
斎藤 | 22 | 東京 |
田尻 | 23 | 東京 |
select *
from `Class_A`
where age < (
select
min(age) from `Class_B`
where city = "群馬"
)
result
name | age | city |
---|
NULLを他の値に変換するCOALESCE
SQL
select *
from `Class_B` LIMIT 0,100
Data
name | age | city |
---|---|---|
山田 | 東京 | |
斎藤 | 22 | 東京 |
田尻 | 23 | 東京 |
SQL
select name, COALESCE(age, 100)
from `Class_B`
order by age desc LIMIT 0,100
Data
name | COALESCE(age, 100) |
---|---|
田尻 | 23 |
斎藤 | 22 |
山田 | 100 |

5 EXISTS句の使い方
同値変換
全てのxがPを満たす = Pを満たさないxが存在しない
後者はEXISTS句で表現することができる
例)
- 全ての教科の点数が50点以上 = 50点以上の教科が存在しない
student_id | subject | score |
---|---|---|
100 | 国語 | 80 |
100 | 理科 | 80 |
100 | 算数 | 100 |
200 | 国語 | 95 |
200 | 算数 | 80 |
300 | 国語 | 90 |
300 | 社会 | 55 |
300 | 算数 | 40 |
400 | 算数 | 80 |
すべての教科の点数が50点以上の生徒 = 50点未満の教科が存在しない
select DISTINCT t1.student_id
from `TestScores` t1
WHERE not EXISTS (
select *
from `TestScores` t2
where t1.student_id = t2.student_id
and t2.score < 50
) LIMIT 0,100
result
student_id |
---|
100 |
200 |
400 |
算数であれば80点以上、国語であれば50点以上を満たす生徒
where句の中でcase文を使って絞り込むのがテクい
慣れないので多分次の章で忘れる
SELECT student_id, subject
FROM TestScores TS1
WHERE subject IN ('算数', '国語')
AND NOT EXISTS
(SELECT *
FROM TestScores TS2
WHERE TS2.student_id = TS1.student_id
-- 算数であれば点数が80点未満・国語であれば点数が50点未満
AND 1 = CASE WHEN subject = '算数' AND score < 80 THEN 1
WHEN subject = '国語' AND score < 50 THEN 1
ELSE 0 END) LIMIT 0,100
student_id | subject |
---|---|
100 | 国語 |
100 | 算数 |
200 | 国語 |
200 | 算数 |
400 | 算数 |
Data
project_id | step_nbr | status |
---|---|---|
AA100 | 0 | 完了 |
AA100 | 1 | 待機 |
AA100 | 2 | 待機 |
B200 | 0 | 待機 |
B200 | 1 | 待機 |
CS300 | 0 | 完了 |
CS300 | 1 | 完了 |
CS300 | 2 | 待機 |
CS300 | 3 | 待機 |
DY400 | 0 | 完了 |
DY400 | 1 | 完了 |
DY400 | 2 | 完了 |
1番の工程まで完了しているproject_idの取得
集合指向的な回答
select
project_id
from `Projects`
group by project_id
having count(*) = sum(
case
when step_nbr <= 1 and status = "完了" then 1
when step_nbr > 1 and status = "待機" then 1
else 0
end
) LIMIT 0,100
project_id |
---|
CS300 |
EXISTSを使用した回答
1番の工程まで完了 and 2番以降の工程が待機中
= 1番の工程まででは待機中・2番以降の工程であれば完了
のものが存在しない
で同値変換できる
select
P1.project_id, P1.step_nbr, P1.status
from `Projects` P1
where not EXISTS (
select *
from `Projects` P2
where P1.project_id = P2.project_id
and 1 =
case
when step_nbr <= 1 and status = "待機" then 1
when step_nbr >= 2 and status = "完了" then 1
else 0
end
) LIMIT 0,100
result
project_id | step_nbr | status |
---|---|---|
CS300 | 0 | 完了 |
CS300 | 1 | 完了 |
CS300 | 2 | 待機 |
CS300 | 3 | 待機 |
havingではなくexistsで書くメリットは
- パフォーマンス
- インデックスを利用する
- exisitsはtrueが返ったらそれ以降スキャンしない
- 残る情報が多い
素数を求める
seq |
---|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
select N1.seq
from `Nums` N1
where not EXISTS (
SELECT *
from `Nums` N2
where N2.seq > 1 and N2.seq < N1.seq
and N1.seq % N2.seq = 0
) LIMIT 0,100
seq |
---|
1 |
2 |
3 |
5 |
7 |
11 |
13 |
17 |
19 |