SQLを使ってデータベースからデータを抽出する際、特定の範囲内に収まるレコードを取得したい場面は非常に多くあります。
そのような場合に頻繁に利用されるのがBETWEEN演算子です。
しかし、実務において「指定した範囲の端の数値や日付は結果に含まれるのか」という疑問を持つ方は少なくありません。
結論から述べますと、SQLのBETWEEN演算子は、指定した開始値と終了値の両方を「含む」という性質を持っています。
この仕様を正確に理解しておくことは、データの抜け漏れや重複を防ぐために不可欠です。
本記事では、数値、日付、文字列といった異なるデータ型におけるBETWEEN演算子の挙動や、実務でハマりやすい注意点について詳しく解説します。
BETWEEN演算子の基本的な仕組み
BETWEEN演算子は、カラムの値が指定された範囲内にあるかどうかを評価するために使用されます。
まずは、その基本的な構文を確認しましょう。
SELECT カラム名
FROM テーブル名
WHERE カラム名 BETWEEN 開始値 AND 終了値;
この構文は、内部的には比較演算子を用いた以下の条件式と全く同じ意味を持ちます。
SELECT カラム名
FROM テーブル名
WHERE カラム名 >= 開始値 AND カラム名 <= 終了値;
ここで重要なのは、開始値に対しては「以上(>=)」、終了値に対しては「以下(<=)」という条件が適用される点です。
つまり、境界値として指定した値そのものも検索結果に含まれます。
この動作は「閉区間」と呼ばれ、ほとんどの主要なデータベース管理システム (RDBMS) で共通の仕様となっています。
BETWEEN演算子と比較演算子の違い
BETWEEN演算子を使うメリットは、コードの可読性が向上することにあります。
特に複数の条件を組み合わせる複雑なクエリでは、比較演算子を並べるよりも直感的に範囲指定を理解できます。
ただし、境界値を含めたくない(未満や超えるを指定したい)場合には、BETWEENではなく比較演算子を使用する必要があります。
数値データにおけるBETWEENの挙動
数値データに対してBETWEENを使用する場合、動作は非常にシンプルで予測しやすいものです。
例えば、商品の価格が 1000円から 5000円のものを取得する場合を考えてみましょう。
-- 価格が1000円以上5000円以下の商品を抽出
SELECT product_name, price
FROM products
WHERE price BETWEEN 1000 AND 5000;
このクエリを実行した際、価格がちょうど 1000 の商品も、ちょうど 5000 の商品も結果に含まれます。
実行結果のイメージは以下の通りです。
product_name | price
-------------+-------
商品A | 1000
商品B | 2500
商品C | 5000
このように、整数や浮動小数点数の範囲指定において、BETWEENは直感どおりの動きをします。
数値指定時の注意点
BETWEEN演算子を使用する際、必ず「開始値 <= 終了値」の順で記述しなければなりません。
もし BETWEEN 5000 AND 1000 のように記述した場合、多くのデータベースではエラーにはなりませんが、結果が 0件として返されます。
これは price >= 5000 AND price <= 1000 という矛盾した条件として解釈されるためです。
日付・時刻データにおけるBETWEENの注意点
SQLのBETWEEN演算子で最もトラブルが起きやすいのが、日付や時刻データの扱いです。
日付型(DATE)と日時型(DATETIME / TIMESTAMP)では、境界値の解釈が異なるように見えることがあります。
DATE型の場合
時刻情報を持たない純粋な日付のみのデータであれば、数値データと同じ感覚で利用できます。
-- 2026年1月1日から2026年1月3日までのデータを抽出
SELECT order_id, order_date
FROM orders
WHERE order_date BETWEEN '2026-01-01' AND '2026-01-03';
この場合、1月1日、1月2日、1月3日のデータがすべて含まれます。
DATETIME型の場合の落とし穴
一方で、データに「時分秒」が含まれる場合は注意が必要です。
例えば、'2026-01-03' という文字列を日時型として扱うと、データベース内部では自動的に '2026-01-03 00:00:00' と解釈されるのが一般的です。
そのため、以下のクエリを実行すると、1月3日の日中に発生したデータが漏れてしまう可能性があります。
-- 意図:1月1日から1月3日いっぱいまでを取得したい
SELECT * FROM logs
WHERE created_at BETWEEN '2026-01-01 00:00:00' AND '2026-01-03 00:00:00';
このクエリでは、2026-01-03 00:00:01 以降のデータは「終了値を超えている」とみなされ、抽出対象から外れてしまいます。
実務では、この問題を回避するために以下のような比較演算子による記述が推奨されることが多いです。
-- 1月3日の23:59:59までを確実に含むための安全な書き方
SELECT * FROM logs
WHERE created_at >= '2026-01-01' AND created_at < '2026-01-04';
このように、「開始日は以上、終了日の翌日は未満」とする手法は、多くのエンジニアが採用しているベストプラクティスです。
文字列データにおけるBETWEENの挙動
BETWEEN演算子は、文字列(VARCHARなど)に対しても使用可能です。
この場合、値は辞書順(アルファベット順や文字コード順)で評価されます。
-- AからCで始まる名前のユーザーを取得
SELECT user_name
FROM users
WHERE user_name BETWEEN 'A' AND 'C';
ここでも「含む」の法則が適用されますが、文字列特有の挙動に注意してください。
上記の場合、’A’ や ‘B’、そしてちょうど ‘C’ という名前のユーザーは含まれます。
しかし、‘Ca’ や ‘Charlie’ という名前のユーザーは含まれない可能性が高いです。
なぜなら、辞書順において ‘Ca’ は ‘C’ よりも後ろに位置すると判定されるため、終了値である ‘C’ を超えているとみなされるからです。
文字列の範囲指定で境界値を厳密に制御したい場合は、BETWEENよりも LIKE や前方一致の活用を検討すべきでしょう。
NOT BETWEENによる範囲外の抽出
BETWEENの否定形である NOT BETWEEN を使用すると、指定した範囲に含まれないデータを抽出できます。
この場合、境界値はどう扱われるのでしょうか。
-- 100から500の範囲「外」にあるデータを抽出
SELECT * FROM products
WHERE price NOT BETWEEN 100 AND 500;
この条件式は、内部的に price < 100 OR price > 500 と等価です。
つまり、NOT BETWEENを使用した場合、指定した境界値(100と500)は結果に含まれません。
範囲「内」を指定する場合は境界を含み、範囲「外」を指定する場合は境界を含まない、と整理して覚えておきましょう。
BETWEEN演算子とNULL値の相関
SQLを扱う上で避けて通れないのが NULL の存在です。
BETWEEN演算子の評価対象となるカラムに NULL が含まれている場合、そのレコードは抽出されません。
SQLにおいて、NULL は「不明な値」を意味し、どのような数値や日付と比較しても結果は UNKNOWN になるためです。
以下の表は、各値の状態に対する BETWEEN 1 AND 10 の評価結果をまとめたものです。
| カラムの値 | 評価結果 | 結果への含有 |
|---|---|---|
| 1 | TRUE | 含まれる |
| 5 | TRUE | 含まれる |
| 10 | TRUE | 含まれる |
| 11 | FALSE | 含まれない |
| NULL | UNKNOWN | 含まれない |
もし、範囲指定しつつ NULL のレコードも取得したい場合は、IS NULL 条件を OR で組み合わせる必要があります。
パフォーマンスとインデックスへの影響
大量のデータを扱う際、BETWEEN演算子がパフォーマンスに与える影響も無視できません。
一般的に、BETWEENが指定されているカラムにインデックスが貼られていれば、範囲スキャン(Range Scan)が効率的に行われます。
これは >= AND <= と同等の処理であるため、オプティマイザは適切にインデックスを利用して高速に検索を実行します。
しかし、データの分布状況によっては、フルスキャンのほうが速いと判断されることもあります。
特に、テーブルの全レコードのうち大部分が BETWEEN の範囲内に収まるようなケースでは、インデックスの効果は薄くなります。
複合インデックスでの注意点
複数のカラムを組み合わせた複合インデックスを使用している場合、BETWEENを使用するカラムの順番に注意が必要です。
範囲検索(BETWEENや比較演算子)を使用すると、それ以降のインデックスカラムは等価検索として機能しなくなります。
クエリの最適化を目指す場合は、実行計画(EXPLAIN)を確認してインデックスが意図通りに使われているかチェックしましょう。
BETWEEN演算子を使う上でのよくある質問
Q: 負の数に対してもBETWEENは使えますか?
はい、使用可能です。
BETWEEN -100 AND 100 のように指定すれば、マイナス100からプラス100までの範囲にある数値を取得できます。
この場合も、小さい値を左側に、大きい値を右側に記述するルールは変わりません。
Q: 複数の値を個別に指定したい場合はどうすればいいですか?
連続した範囲ではなく、飛び石の値を指定したい場合は BETWEEN ではなく IN 句を使用します。
-- 1, 3, 5のいずれかに合致するデータを抽出
SELECT * FROM users
WHERE id IN (1, 3, 5);
用途に応じて、範囲指定の BETWEEN と複数値指定の IN を使い分けることが重要です。
Q: BETWEENの結果をソートすることはできますか?
もちろん可能です。
WHERE 句で BETWEEN を使ってデータを絞り込んだ後、最後に ORDER BY 句を記述します。
SELECT * FROM products
WHERE price BETWEEN 1000 AND 5000
ORDER BY price DESC;
まとめ
SQLの BETWEEN 演算子は、「開始値以上、かつ終了値以下」という、境界値を両方含む範囲指定を行います。
数値データの抽出では非常にシンプルで強力なツールとなりますが、日時データや文字列データにおいては、境界値の定義に注意が必要です。
特に、時分秒を含む DATETIME 型を扱う際は、意図せず終了日のデータが漏れてしまうといったミスが起こりがちです。
確実なデータ抽出を行うためには、BETWEEN の性質を正しく理解した上で、必要に応じて比較演算子(>= や <)を柔軟に使い分けるスキルが求められます。
今回の内容を参考に、SQLクエリの正確性と可読性を高めていただければ幸いです。
