コマンド道場

BETWEEN 述語の使い方

範囲の中に入る行だけが残る

shain社員

namesalary
中村530000
井上505000
佐藤720000
加藤610000

salary BETWEEN 400000 AND 530000範囲に入った行

namesalary
中村530000
井上505000

WHERE salary BETWEEN 400000 AND 530000

  • 取り消し線= 条件に合わないので結果に出てこない行
  • 社員 18 行 → 範囲に入った行 126 行が消えます)

18 行のうち 12 行が残ります。上限ちょうどの 530000 の行が残っているのが見えます(両端を含むため)。

`BETWEEN` は「以上かつ以下」をひとまとめに書く述語です。 いちばん大事な事実は 両端を含むことと、上下を逆に書くと 0 行になることです。そして時刻を持つ列に使うと、最終日のデータが落ちます。

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

未確認: IBM Db2

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

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

01両端を含む(以上かつ以下)

結論: `列 BETWEEN a AND b` は `列 >= a AND 列 <= b` と完全に同じ意味です。

つまり a も b も範囲に入ります。「a より大きく b より小さい」ではありません。ここを取り違えると、境界の 1 件がずれます。

BETWEEN を使う利点は、列名を 1 回しか書かなくてよいことです。列名が長いときや式を書くときに効きます。逆に「以上かつ未満」にしたいときは BETWEEN では書けないので、>=< を並べます。

読みやすさのために BETWEEN を選び、境界の扱いが違うときは素直に比較演算子を並べる、と使い分けてください。

1 行ずつ、何をしているか
  1. SELECT name, salaryどの列を出すか
  2. FROM shainどの表を見るか
  3. WHERE salary BETWEEN 400000 AND 530000400000 以上かつ 530000 以下
  4. ORDER BY salary並び順

小さいほうを先、大きいほうを後に書きます。順番には意味があります(次の節)。

BETWEEN で書く
SELECT name, salary
FROM shain
WHERE salary BETWEEN 400000 AND 530000
ORDER BY salary;
namesalary
渡辺400000
松本421000
大野432000
小林445050
斎藤455000
吉田468000
高橋480000
井上505000
山本512000
田中520000
伊藤530000
中村530000

12

12 行です。先頭の渡辺はちょうど 400000、末尾の中村と伊藤はちょうど 530000 で、どちらも残っています。

比較演算子で書く(結果は同じ 12 行)
SELECT name, salary
FROM shain
WHERE salary >= 400000 AND salary <= 530000
ORDER BY salary;
namesalary
渡辺400000
松本421000
大野432000
小林445050
斎藤455000
吉田468000
高橋480000
井上505000
山本512000
田中520000
伊藤530000
中村530000

12

同じ 12 行です。BETWEEN は書き方が短くなるだけで、意味は変わりません。

02⚠️ よくある間違い:上下を逆に書くと 0 行になる

結論: `BETWEEN 大きい値 AND 小さい値` と書くと、エラーにならずに 0 行が返ります。

BETWEEN 530000 AND 400000salary >= 530000 AND salary <= 400000 に展開されます。両方を満たす値は存在しないので、必ず 0 行です。

エラーが出ないのが厄介な点です。 「該当なし」という結果は一見もっともらしいので、変数で範囲を渡している場合などは、値の入れ違いに長く気づけません。

範囲を変数から組み立てるときは、渡す前に大小を揃えるか、>=<= を明示的に並べて書いてください。

上下が逆(0 行・エラーにはならない)
SELECT name, salary
FROM shain
WHERE salary BETWEEN 530000 AND 400000;
namesalary

0

1 行も返りません。該当者がいないのではなく、条件そのものが成立していません。

03日付にも文字列にも使える

結論: 大小を比べられるものなら何にでも使えます。 日付でも文字列でも構いません。

日付の期間指定は BETWEEN のいちばん多い使い道です。ただしその列が日付だけを持つのか、時刻も持つのかで結果が変わります(次の節)。

文字列にも使えますが、比較の順序は五十音順ではなく文字コード順です。日本語の文字列に範囲指定を使うと、直感と違う行が入ってきます。文字列の範囲指定は避けるほうが無難です。

2020 年に入社した社員
SELECT name, hired_on
FROM shain
WHERE hired_on BETWEEN DATE '2020-01-01' AND DATE '2020-12-31'
ORDER BY hired_on;
namehired_on
中村2020-04-01
井上2020-10-01

2

2 行です。この表の hired_on は時刻を持たない日付型なので、12/31 が正しく含まれます。

⚠️ この書き方は SQLite / SQL Server / Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)。製品ごとの対応表

文字列の範囲は直感どおりにならない
SELECT busho_name
FROM busho
WHERE busho_name BETWEEN '営' AND '管'
ORDER BY busho_id;
busho_name
営業
基盤
法務

3

営業・基盤・法務が返ります。五十音では「基盤」も「法務」も範囲の外に思えますが、文字コード上はこの 3 つが範囲に入ります。

04⚠️ よくある間違い:時刻を持つ列では最終日が落ちる

結論: 時刻を持つ列に `BETWEEN '開始日' AND '終了日'` と書くと、終了日の 00:00:00 より後のデータが全部落ちます。

2020-12-31 とだけ書くと、多くの製品はこれを 2020-12-31 00:00:00 として扱います。したがって 12/31 の午前 10 時に登録されたデータは、その時点より後なので範囲に入りません。1 日ぶん近くのデータが黙って消えることになります。

この表の hired_on は時刻を持たない日付型なので問題は起きませんが、注文日時・更新日時のような列では必ず起きます

対処は「翌日の 0 時未満」で書くことです。

  • BETWEEN DATE '2020-01-01' AND DATE '2020-12-31'
  • >= DATE '2020-01-01' AND < DATE '2021-01-01'

この形なら、列が日付でも日時でも同じ結果になります。期間指定は `>=` と `<` で書くのを既定にしてください。

12/31 の 10 時は「2020 年の範囲」に入らない
SELECT TIMESTAMP '2020-12-31 10:00'
       BETWEEN TIMESTAMP '2020-01-01'
           AND TIMESTAMP '2020-12-31' AS hairu;
hairu
false

1

false が返ります。終了日が 12/31 の 0 時ちょうどとして扱われるためです。

翌日未満で書けば、日付でも日時でも同じ結果になる
SELECT name, hired_on
FROM shain
WHERE hired_on >= DATE '2020-01-01'
  AND hired_on <  DATE '2021-01-01'
ORDER BY hired_on;
namehired_on
中村2020-04-01
井上2020-10-01

2

2 行で、BETWEEN で書いたときと同じ結果です。列の型が時刻付きに変わっても、この書き方なら結果は変わりません。

⚠️ この書き方は SQLite / SQL Server / Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)。製品ごとの対応表

05⚠️ よくある間違い:NOT BETWEEN は NULL の行を消す

結論: `NOT BETWEEN` を使うと、その列が NULL の行が黙って消えます。

NOT BETWEENNOT (列 >= a AND 列 <= b) に展開されます。列が NULL なら比較は unknown になり、否定しても unknown のままなので、WHERE を通れません。

NOT IN のときとまったく同じ仕組みです。「範囲の外」を出したいのに件数が足りないときは、その列に NULL があるかを確かめてください。

NULL も含めたいなら OR 列 IS NULL を明示的に足します。

所属が未設定の 2 人が出てこない
SELECT name, busho_id
FROM shain
WHERE busho_id NOT BETWEEN 2 AND 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 BETWEEN 2 AND 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 回目以降はキャッシュされます)。

解いてみる

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

関連するトピック

根拠(一次情報)