コマンド道場

IN 述語の使い方

並べた値のどれかに当てはまる行が残る

shain社員

namebusho_id
中村2
伊藤3
井上7
加藤5

busho_id IN (2, 3)どれかに当てはまった行

namebusho_id
中村2
伊藤3

WHERE busho_id IN (2, 3)

  • 取り消し線= 条件に合わないので結果に出てこない行
  • 社員 18 行 → どれかに当てはまった行 810 行が消えます)

18 行のうち残るのは 8 行です。busho_id = 2 OR busho_id = 3 と書いたのとまったく同じ結果になります。

`IN` は「このどれかに当てはまる」を短く書くための述語です。 OR を並べたものと同じ意味になります。ただし否定した NOT IN には、NULL が 1 つ混ざるだけで結果が 0 行になるという有名な落とし穴があります。

この書き方が使えない製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database

未確認: IBM Db2

製品ごとの対応表(根拠つき)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01OR を並べる代わりに使う

結論: `列 IN (値1, 値2, …)` は `列 = 値1 OR 列 = 値2 OR …` と同じ意味です。

候補が 2 つなら OR でも大差ありませんが、5 つ 10 つと増えると IN のほうが圧倒的に読みやすくなります。列名を何度も書かずに済むので、列名を打ち間違える事故も減ります

値は数でも文字列でも構いません。並べる値は同じ種類にしてください。

候補が 1 つだけのときは = で十分ですが、IN で書いても間違いではありません(あとから候補を足しやすい形になります)。

1 行ずつ、何をしているか
  1. SELECT name, busho_idどの列を出すか
  2. FROM shainどの表を見るか
  3. WHERE busho_id IN (2, 3)この並びのどれかに一致する行を残す
  4. ORDER BY shain_id並び順

IN の右側は「値の並び」でも「副問い合わせ」でも構いません。どちらも「この集合に含まれるか」を聞いています。

IN で書く
SELECT name, busho_id
FROM shain
WHERE busho_id IN (2, 3)
ORDER BY shain_id;
namebusho_id
佐藤2
田中2
中村2
小林2
山田3
高橋3
伊藤3
松本3

8

OR で書く(結果は同じ 8 行)
SELECT name, busho_id
FROM shain
WHERE busho_id = 2 OR busho_id = 3
ORDER BY shain_id;
namebusho_id
佐藤2
田中2
中村2
小林2
山田3
高橋3
伊藤3
松本3

8

同じ 8 行です。どちらで書いても結果は変わりません。読みやすさで選んでください。

文字列でも使える
SELECT busho_id, busho_name
FROM busho
WHERE busho_name IN ('開発', '営業')
ORDER BY busho_id;
busho_idbusho_name
2開発
3営業

2

02右側に副問い合わせを書ける

結論: `IN` の右側には、1 列だけを返す `SELECT` をそのまま置けます。

値を手で並べる代わりに「別の表から取ってきた集合」を渡せます。候補が実行時にしか分からないときは、こちらが基本形になります。

条件は 1 つだけ、副問い合わせが返す列は 1 列であることです。複数列を返すとエラーになります。

値の並びと副問い合わせでは、対応している製品の範囲が少し違います。使えない製品がある例には自動で注記が出ます。

「経営の直下の部署」に所属する社員
SELECT name
FROM shain
WHERE busho_id IN (SELECT busho_id
                   FROM busho
                   WHERE parent_busho_id = 1)
ORDER BY shain_id;
name
佐藤
田中
中村
小林
山田
高橋
伊藤
松本

11 … うち先頭 8 行を表示

11 行返ります。部署表から親が経営(busho_id = 1)である部署の番号を集めて、その集合に含まれる社員を出しています。部署が増減しても SQL を書き換える必要がありません。

03⚠️ よくある間違い:NOT IN は NULL 1 つで 0 行になる

結論: `NOT IN` の右側に NULL が 1 つでも混ざると、結果は必ず 0 行になります。

理由は展開すると分かります。x NOT IN (a, b, NULL) は次と同じ意味です。

  • x <> a AND x <> b AND x <> NULL

最後の x <> NULL は、x が何であっても unknown です。AND は 1 つでも unknown が混ざると true になれない(false か unknown にしかならない)ので、どの行も残りません

危ないのは副問い合わせを渡したときです。その列に NULL が入っていることに気づかないまま NOT IN を書くと、エラーも警告も出ずに 0 行が返ります。「該当なし」という正しそうに見える嘘の答えが返るのが最悪の点です。

対処は `NOT EXISTS` を使うことです。 NOT EXISTS は「一致する行が 1 つも無いか」を聞くだけなので、NULL の影響を受けません。

「社員が 1 人もいない部署」を NOT IN で(0 行)
SELECT busho_name
FROM busho
WHERE busho_id NOT IN (SELECT busho_id FROM shain);
busho_name

0

実際には法務が該当するのに、0 行が返ります。shain.busho_id には所属未設定の社員 2 人ぶんの NULL が入っているためです。

NOT EXISTS なら正しく出る
SELECT busho_name
FROM busho b
WHERE NOT EXISTS (SELECT 1
                  FROM shain s
                  WHERE s.busho_id = b.busho_id);
busho_name
法務

1

法務が 1 行返ります。同じことを聞いているのに結果が違うのは、NULL の扱いが違うからです。**否定するときは NOT EXISTS を既定にしてください。**

04⚠️ よくある間違い:値の並びでも同じことが起きる

結論: 手で並べた値に NULL を書いても同じです。 そして IN(否定なし)のほうにも別の注意があります。

  • busho_id IN (2, NULL) … NULL に一致する行は無いので、実質 IN (2) と同じ結果になります。NULL の行が出てくることはありません
  • busho_id NOT IN (2, NULL) … 0 行になります

否定していない IN にも注意が要ります。列そのものが NULL の行は、並びに何を書いても一致しません。 「所属が未設定の社員」を IN の並びで取り出すことはできないので、IS NULL を別に足してください。

所属が未設定の 2 人が出てこない
SELECT name, busho_id
FROM shain
WHERE busho_id NOT IN (2, 3)
ORDER BY shain_id;
namebusho_id
鈴木1
加藤5
吉田5
井上7
木村7
清水4
斎藤4
山本4

8

8 行です。所属が 2 でも 3 でもない社員は本来 10 人いますが、渡辺と大野(所属未設定)が落ちています。

IS NULL を足すと戻ってくる
SELECT name, busho_id
FROM shain
WHERE busho_id NOT IN (2, 3)
   OR busho_id IS NULL
ORDER BY shain_id;
namebusho_id
鈴木1
加藤5
吉田5
井上7
木村7
清水4
斎藤4
山本4
渡辺NULL
大野NULL

10

10 行になりました。増えた 2 行が所属未設定の社員です。

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

  • IN / EXISTS(練習問題)IN と EXISTS の使い分けを、もう少し踏み込んで扱います。
  • AND / OR / NOTNOT IN が 0 行になる理由は AND と unknown の規則そのものです。
  • IS NULLNULL の行を取り出すには IS NULL が要ります。
  • BETWEEN 述語「範囲のどこか」を聞くときはこちらを使います。

根拠(一次情報)