SQLのINとBETWEENとは ― 使い方とNOT IN
SQLの
SQLのINとBETWEENとは、WHERE句で値の候補や範囲を指定する述語
SQLのINは、列の値が一覧に並べた値のどれかに等しい行を選ぶ述語(条件を表す語)です。BETWEENは、列の値が下限以上かつ上限以下の範囲に入る行を選びます。
どちらもSELECT文のWHERE句で、価格が500円・1000円・3000円のどれかの商品や、1000円から3000円の商品を選ぶときに使います。LIKEは文字列の一部で行を選びますが、INとBETWEENは値の候補や大小の範囲で行を選びます。
SELECT 商品名 FROM 商品
WHERE 価格 IN (500, 1000, 3000); -- 3つの価格のどれか
SELECT 商品名 FROM 商品
WHERE 価格 BETWEEN 1000 AND 3000; -- 1000以上3000以下次の商品の表を例にします。上の1つ目の文はUSBケーブル・マウス・Webカメラを、2つ目の文はマウスからWebカメラまでの4行を返します。
| 商品名 | 価格 |
|---|---|
| USBケーブル | 500 |
| マウス | 1000 |
| スピーカー | 2000 |
| キーボード | 2500 |
| Webカメラ | 3000 |
| ヘッドセット | 4800 |
INはORの連続、BETWEENは>=と<=のANDと同じ
INは=をORでつないだ条件の略記で、BETWEENは>=と<=をANDでつないだ条件の略記です。PostgreSQLの文書も、INを=のOR連結の略記として説明し、BETWEENは a >= x AND a <= y と同じだと説明しています。
| 述語 | 同じ | 残る |
|---|---|---|
| 価格 IN (500, 1000, 3000) | 価格 = 500 OR 価格 = 1000 OR 価格 = 3000 | USBケーブル・マウス・Webカメラ |
| 価格 BETWEEN 1000 AND 3000 | 価格 >= 1000 AND 価格 <= 3000 | マウス・スピーカー・キーボード・Webカメラ |
| 価格 NOT IN (500, 1000) | 価格 <> 500 AND 価格 <> 1000 | スピーカー・キーボード・Webカメラ・ヘッドセット |
| 価格 NOT BETWEEN 1000 AND 3000 | 価格 < 1000 OR 価格 > 3000 | USBケーブル・ヘッドセット |
どちらの書き方でも、残る行は同じです。PostgreSQLで実行すると、表の4組はどれも同じ行を返しました。候補が多いときはINのほうが短く書け、列名を書き間違える箇所も減ります。
MySQLの文書は、INの一覧に引用符付きの値と数値を混ぜないように注意を促しています。文字列と数値では比べ方の決まりが違い、結果がそろわないことがあるためです。一覧の値は、列と同じ型にそろえて書きます。
条件を選んで、表のどの行が残るかを確かめる
次の図では、最初に示した商品の表にINやBETWEENの条件を試せます。条件を選ぶと、ORやANDに書き換えた式の各行の判定と、結果に残る行が切り替わります。結果は、PostgreSQLで実際に実行して確かめたものです。
SELECT 商品名 FROM 商品
WHERE 価格 IN (500, 1000, 3000);価格 = 500 OR 価格 = 1000 OR 価格 = 3000- USBケーブル500
TRUE OR FALSE OR FALSE → TRUE残る - マウス1000
FALSE OR TRUE OR FALSE → TRUE残る - スピーカー2000
FALSE OR FALSE OR FALSE → FALSE除かれる - キーボード2500
FALSE OR FALSE OR FALSE → FALSE除かれる - Webカメラ3000
FALSE OR FALSE OR TRUE → TRUE残る - ヘッドセット4800
FALSE OR FALSE OR FALSE → FALSE除かれる
SELECT 商品名 FROM 商品
WHERE 価格 BETWEEN 1000 AND 3000;価格 >= 1000 AND 価格 <= 3000- USBケーブル500
FALSE AND TRUE → FALSE除かれる - マウス1000
TRUE AND TRUE → TRUE残る - スピーカー2000
TRUE AND TRUE → TRUE残る - キーボード2500
TRUE AND TRUE → TRUE残る - Webカメラ3000
TRUE AND TRUE → TRUE残る - ヘッドセット4800
TRUE AND FALSE → FALSE除かれる
SELECT 商品名 FROM 商品
WHERE 価格 > 1000 AND 価格 < 3000;- USBケーブル500
FALSE AND TRUE → FALSE除かれる - マウス1000
FALSE AND TRUE → FALSE除かれる - スピーカー2000
TRUE AND TRUE → TRUE残る - キーボード2500
TRUE AND TRUE → TRUE残る - Webカメラ3000
TRUE AND FALSE → FALSE除かれる - ヘッドセット4800
TRUE AND FALSE → FALSE除かれる
SELECT 商品名 FROM 商品
WHERE 価格 BETWEEN 3000 AND 1000;価格 >= 3000 AND 価格 <= 1000- USBケーブル500
FALSE AND TRUE → FALSE除かれる - マウス1000
FALSE AND TRUE → FALSE除かれる - スピーカー2000
FALSE AND FALSE → FALSE除かれる - キーボード2500
FALSE AND FALSE → FALSE除かれる - Webカメラ3000
TRUE AND FALSE → FALSE除かれる - ヘッドセット4800
TRUE AND FALSE → FALSE除かれる
SELECT 商品名 FROM 商品
WHERE 価格 NOT BETWEEN 1000 AND 3000;価格 < 1000 OR 価格 > 3000- USBケーブル500
TRUE OR FALSE → TRUE残る - マウス1000
FALSE OR FALSE → FALSE除かれる - スピーカー2000
FALSE OR FALSE → FALSE除かれる - キーボード2500
FALSE OR FALSE → FALSE除かれる - Webカメラ3000
FALSE OR FALSE → FALSE除かれる - ヘッドセット4800
FALSE OR TRUE → TRUE残る
SELECT 商品名 FROM 商品
WHERE 価格 NOT IN (500, 1000);価格 <> 500 AND 価格 <> 1000- USBケーブル500
FALSE AND TRUE → FALSE除かれる - マウス1000
TRUE AND FALSE → FALSE除かれる - スピーカー2000
TRUE AND TRUE → TRUE残る - キーボード2500
TRUE AND TRUE → TRUE残る - Webカメラ3000
TRUE AND TRUE → TRUE残る - ヘッドセット4800
TRUE AND TRUE → TRUE残る
SELECT 商品名 FROM 商品
WHERE 価格 IN (500, 1000, NULL);価格 = 500 OR 価格 = 1000 OR 価格 = NULL- USBケーブル500
TRUE OR FALSE OR UNKNOWN → TRUE残る - マウス1000
FALSE OR TRUE OR UNKNOWN → TRUE残る - スピーカー2000
FALSE OR FALSE OR UNKNOWN → UNKNOWN除かれる - キーボード2500
FALSE OR FALSE OR UNKNOWN → UNKNOWN除かれる - Webカメラ3000
FALSE OR FALSE OR UNKNOWN → UNKNOWN除かれる - ヘッドセット4800
FALSE OR FALSE OR UNKNOWN → UNKNOWN除かれる
SELECT 商品名 FROM 商品
WHERE 価格 NOT IN (500, 1000, NULL);価格 <> 500 AND 価格 <> 1000 AND 価格 <> NULL- USBケーブル500
FALSE AND TRUE AND UNKNOWN → FALSE除かれる - マウス1000
TRUE AND FALSE AND UNKNOWN → FALSE除かれる - スピーカー2000
TRUE AND TRUE AND UNKNOWN → UNKNOWN除かれる - キーボード2500
TRUE AND TRUE AND UNKNOWN → UNKNOWN除かれる - Webカメラ3000
TRUE AND TRUE AND UNKNOWN → UNKNOWN除かれる - ヘッドセット4800
TRUE AND TRUE AND UNKNOWN → UNKNOWN除かれる
BETWEEN 1000 AND 3000 と、>と<で書いた条件を比べると、両端の扱いの違いが分かります。NOT INの一覧にNULLを足すと、それまで残っていた4行が全部消えます。
BETWEENは両端を含み、小さい値を先に書く
BETWEEN 1000 AND 3000 は、1000ちょうどと3000ちょうどの行も選びます。日本語に直すと、1000円以上3000円以下です。PostgreSQLの文書も、BETWEENは範囲の端の値を範囲に含めると説明しています。
端を含めたくないときは、BETWEENを使わずに>と<で書きます。価格 > 1000 AND 価格 < 3000 なら、1000円より高く3000円より安い行だけが残ります。
上限を先に書くと、エラーにならずに0行になる
BETWEENは、ANDの左に下限を、右に上限を書きます。逆に価格 BETWEEN 3000 AND 1000 と書くと、3000以上かつ1000以下の値を探すことになります。そのような値は無いので、エラーは出ず、結果は0行になります。
PostgreSQLの文書でも、MySQLの文書でも、2 BETWEEN 3 AND 1 の結果は偽です。PostgreSQLには、2つの値を小さい順に並べ替えてから比べる BETWEEN SYMMETRIC もあります。MySQLの文書のBETWEENの説明には、この書き方は出てきません。
日付や文字列でBETWEENを使うときの注意
日時の列に日付だけで上限を書くと、最終日の0時より後が漏れる
10月の注文を探すつもりで、日時の列に次の条件を書いたとします。PostgreSQLで6件の注文の表に実行すると、10月31日の14時20分の注文が結果に入りませんでした。
WHERE 注文日時 BETWEEN '2026-10-01' AND '2026-10-31'
-- 10/31 0:00 までしか入らない(10/31 14:20 の注文は外れる)
WHERE 注文日時 >= '2026-10-01' AND 注文日時 < '2026-11-01'
-- 10/31 の全ての時刻が入る日時の列と比べるとき、日付だけの '2026-10-31' は10月31日の0時0分として扱われます。BETWEENの範囲はその時刻で終わるので、同じ日の0時より後の注文は外れます。
日時の列で日の範囲を選ぶときは、翌日の0時を<で上限にします。この書き方なら、最終日の何時の注文でも範囲に入ります。列が時刻を持たない日付の型なら、BETWEEN '2026-10-01' AND '2026-10-31' で31日の行も入ります。
MySQLの文書も、日付や時刻にBETWEENを使うときは、CAST()で値の型をそろえるように勧めています。たとえば日時の列を2つの日付と比べるなら、日付の側を日時の型に直します。
文字列のBETWEENは、文字の並び順で範囲を決める
BETWEENの中身は大小の比較なので、大小を比べられる文字列にも使えます。文字列の大小は、辞書のように先頭の文字から順に比べて決まります。
WHERE 名前 BETWEEN 'A' AND 'C' -- Alice・Bob・C(Carolは入らない)
WHERE 名前 >= 'A' AND 名前 < 'D' -- Alice・Bob・C・CarolCarolは、Cの1文字より後ろに並びます。そのため、上限を 'C' にするとCarolは範囲から外れます。Cで始まる名前まで含めたいときは、次の文字のDを<で上限にします。
大文字と小文字のどちらを先に並べるかは、照合順序によって変わります。照合順序は文字の比べ方の決まりで、製品と設定ごとに違います。LIKEで大文字と小文字を区別するかどうかも照合順序で決まります。詳しくはLIKEの記事で説明しています。
NOT INとNOT BETWEENは、条件に合わない行を選ぶ
NOT INは、一覧のどの値とも等しくない行を選びます。NOT BETWEENは、範囲の外にある行を選びます。どちらも、INやBETWEENの結果を逆にした条件です。
WHERE 価格 NOT IN (500, 1000) -- スピーカー・キーボード・Webカメラ・ヘッドセット
WHERE 価格 NOT BETWEEN 1000 AND 3000 -- USBケーブル・ヘッドセットNOT BETWEEN 1000 AND 3000 は、価格 < 1000 OR 価格 > 3000 と同じです。BETWEENが両端を含むので、NOT BETWEENでは1000ちょうどと3000ちょうどの行が除かれます。NOT INは、<>をANDでつないだ条件になります。
否定の条件では、列の値がNULLの行も選ばれません。理由と、NULLの行も残す書き方はSQLのNULLの記事で説明しています。
NOT INの一覧にNULLがあると、1行も残らない
NOT INの一覧にNULLが1つでも入っていると、結果は0行になります。価格 NOT IN (500, 1000, NULL) は、次のようにANDでつないだ条件と同じです。
価格 <> 500 AND 価格 <> 1000 AND 価格 <> NULL価格が2000のスピーカーでは、最初の2つはTRUEです。しかし最後の 2000 <> NULL は、NULLとの比較なのでUNKNOWNになります。TRUEとUNKNOWNをANDでつなぐとUNKNOWNになり、WHERE句はこの行を残しません。
500や1000の行は、最初の比較がFALSEなので、AND全体もFALSEです。こうして全ての行がFALSEかUNKNOWNになります。UNKNOWNとANDの組み合わせの決まりは、SQLのNULLの記事の3値論理の表で確かめられます。
一方、INの一覧のNULLは結果を変えません。価格 IN (500, 1000, NULL) は、500と1000の行をTRUEにします。ほかの行はUNKNOWNになりますが、FALSEのときと同じく結果から外れるからです。
PostgreSQLとMySQLの文書は、どちらもこの扱いを説明しています。一致する値が無く、一覧にNULLがあるとき、INの結果はNULLになります。一覧を手で書くならNULLを入れなければ済みますが、サブクエリ(SQL文の中に書いたSELECT文)で一覧を作るときは、NULLの混入に気づきにくくなります。
INの右側には、サブクエリの結果も書ける
INの右側には、値を並べる代わりにSELECT文を書けます。内側のSELECT文が返した1列の値が、そのまま一覧として使われます。
SELECT 商品名 FROM 商品
WHERE 商品名 IN (
SELECT 商品名 FROM 注文明細 -- マウス・Webカメラ・マウス
); -- 結果:マウス・Webカメラ注文明細にマウスが2回出ても、商品の表のマウスは1行として1回だけ残ります。内側の結果にNULLが混じると、NOT INでは前の節と同じことが起きます。NOT EXISTSを使って避ける方法を、サブクエリの記事で説明しています。
INやBETWEENの条件の列にB-treeのインデックスがあれば、索引で探せる場合があります。PostgreSQLの文書は、BETWEENとINを、B-treeの索引で探せる書き方の例に挙げています。索引の仕組みはインデックスの記事で説明しています。
参考にした資料
- PostgreSQL 18 文書「Comparison Functions and Operators」(BETWEEN・NOT BETWEEN・BETWEEN SYMMETRIC)
- PostgreSQL 18 文書「Row and Array Comparisons」(IN・NOT INの書き換えとNULL)
- PostgreSQL 18 文書「Subquery Expressions」(サブクエリを使うIN)
- PostgreSQL 18 文書「Index Types」(BETWEENとINとB-tree)
- MySQL 8.4 Reference Manual「Comparison Functions and Operators」(BETWEENと日付、IN・NOT INとNULL)
練習問題(全5問)
正解:価格 >= 1000 AND 価格 <= 3000
BETWEENは
正解:0行に
BETWEENは、
正解:10月31日 14:20の
'2026-10-31' は
正解:0行に
NOT INは、
正解:商品名 = 'マウス' OR 商品名 = 'キーボード'
INは、
選んでから「採点する」を押すと、解説つきで答え合わせができます。
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

