BETWEEN a AND b は >= a AND <= b と同じ意味です。この記事の出力は MySQL 8.4.6 で採りました。
まず見る(結論)
BETWEEN 100 AND 500は 100 と 500 ちょうどの行も含む- 下限と上限を逆に書くと、エラーにならずに0件になる
- DATETIME の列を
BETWEEN '2026-10-01' AND '2026-10-31'で絞ると、10月31日 0時0分より後の行が入らない - 日時の範囲は
>= '2026-10-01' AND < '2026-11-01'と書く
例の表 orders です。
+----+-------------+--------+---------------------+
| id | customer_id | amount | ordered_at |
+----+-------------+--------+---------------------+
| 1 | 1 | 100.00 | 2026-10-01 00:00:00 |
| 2 | 2 | 250.00 | 2026-10-15 12:00:00 |
| 3 | 3 | 500.00 | 2026-10-31 13:00:00 |
| 4 | 4 | 50.00 | 2026-09-30 23:59:59 |
| 5 | 5 | 600.00 | 2026-11-01 00:00:00 |
+----+-------------+--------+---------------------+
境界値は含まれる
mysql> SELECT * FROM orders WHERE amount BETWEEN 100 AND 500;
+----+-------------+--------+---------------------+
| id | customer_id | amount | ordered_at |
+----+-------------+--------+---------------------+
| 1 | 1 | 100.00 | 2026-10-01 00:00:00 |
| 2 | 2 | 250.00 | 2026-10-15 12:00:00 |
| 3 | 3 | 500.00 | 2026-10-31 13:00:00 |
+----+-------------+--------+---------------------+
範囲の外を取る NOT BETWEEN 100 AND 500 では、残りの 50.00 と 600.00 の2行が返りました。
下限と上限を逆にすると0件
BETWEEN 500 AND 100 は >= 500 AND <= 100 なので、どの行も満たしません。エラーにも警告にもならず、0件が返ります。画面の入力値から範囲を組み立てる処理では、大小が逆になっていないかを確かめます。
日時の範囲で最終日が漏れる
10月の注文を取るつもりで書いた SQL です。
mysql> SELECT id, amount, ordered_at FROM orders
-> WHERE ordered_at BETWEEN '2026-10-01' AND '2026-10-31';
+----+--------+---------------------+
| id | amount | ordered_at |
+----+--------+---------------------+
| 1 | 100.00 | 2026-10-01 00:00:00 |
| 2 | 250.00 | 2026-10-15 12:00:00 |
+----+--------+---------------------+
10月31日 13:00 の id 3 が入っていません。DATETIME と比べるとき、'2026-10-31' は 2026-10-31 00:00:00 として扱われるためです。終わりを「翌月1日より前」にすると、31日の行も入ります。
mysql> SELECT id, amount, ordered_at FROM orders
-> WHERE ordered_at >= '2026-10-01' AND ordered_at < '2026-11-01';
+----+--------+---------------------+
| id | amount | ordered_at |
+----+--------+---------------------+
| 1 | 100.00 | 2026-10-01 00:00:00 |
| 2 | 250.00 | 2026-10-15 12:00:00 |
| 3 | 500.00 | 2026-10-31 13:00:00 |
+----+--------+---------------------+
BETWEEN '2026-10-01' AND '2026-10-31 23:59:59' と書く方法もありますが、列が秒未満を持つ型(DATETIME(3) など)だと 23:59:59.5 のような値が漏れます。< で翌日を指定する書き方なら型に関係なく漏れません。
文字列の BETWEEN
文字列は辞書順で比べます。名前が a、b、c、cat、d の5人に BETWEEN 'a' AND 'c' を使うと、a、b、c の3人が返り、cat は入りませんでした。'cat' は 'c' より後ろに並ぶためです。「c で始まる名前まで」を取りたいなら、>= 'a' AND < 'd' と書きます。
索引は使われる
ordered_at に索引を作ってから、日時を BETWEEN で絞る検索を EXPLAIN すると type が range になり、索引で範囲を読んでいました。
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | orders | range | idx_orders_ordered_at | idx_orders_ordered_at | 6 | NULL | 2 | 100.00 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
(partitions 列を省いています) DATE(ordered_at) BETWEEN ... のように列を関数で包むと、索引の値と直接比べられなくなるので避けます。