面試官:MySQL BETWEEN AND 語句包括邊界嗎?
使用數據庫時,我們經常會用到范圍查詢,今天來聊一聊 MySQL 的 BETWEEN AND 語句。
1.是否包含邊界
為了說明 BETWEEN AND 語句是否包括邊界,我們先創建一張表,SQL 如下:
CREATE TABLE`test3` (
`id`int(8) NOTNULL AUTO_INCREMENT,
`a`varchar(10) COLLATE utf8_bin DEFAULTNULL,
`b`varchar(10) COLLATE utf8_bin DEFAULTNULL,
`date`dateDEFAULTNULL,
`date_time` datetime DEFAULTNULL,
`time_stamp`timestampNULLDEFAULTNULL,
PRIMARY KEY (`id`),
KEY`idx_a` (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=11DEFAULTCHARSET=utf8 COLLATE=utf8_bin我們插入 10 條數據,如下圖:
圖片
圖片
1.1 執行結果
- 對 id 做范圍查詢
SELECT * FROM test3 WHERE id BETWEEN 2 AND 6;查詢結果如下:
圖片
圖片
可以看到,對 id 做范圍查詢,包含了邊界值 2 和 6,是一個閉區間。
- 對 date 做范圍查詢
SELECT * FROM test3 WHERE DATE BETWEEN '2025-08-28' AND '2025-08-31';查詢結果如下:
圖片
圖片
可以看到,對 date 類型的字段進行范圍查詢,同樣包含了邊界值,是一個閉區間。
- 對 date_time 做范圍查詢
SELECT * FROM test3 WHERE date_time BETWEEN '2025-08-28' AND '2025-08-31';對 date_time 使用日期做范圍查詢,查詢結果如下:
圖片
圖片
可以看到,這個查詢漏掉了 2025-08-31 這一天的數據,是一個前閉后開的區間。
這是因為 MySQL 會自動將日期的時間部分補充為零點,所以上面的查詢語句等價于:
SELECT * FROM test3 WHERE date_time BETWEEN '2025-08-28 00:00:00' AND '2025-08-31 00:00:00';為了不漏掉數據,可以將上面的查詢語句改為:
SELECT * FROM test3 WHERE DATE BETWEEN '2025-08-28' AND '2025-09-01';對 time_stamp 使用日期做范圍查詢,結果跟 date_time 一樣。
1.2 總結
- BETWEEN AND 語句是包括邊界值的,是一個前閉后閉的區間。BETWEEN A AND B 等價于 >= A AND <= B。
- 注意時間類型的字段,DATETIME 和 TIMESTAMP,如果采用日期格式來查詢,結果是前閉后開的區間。BETWEEN A AND B 等價于 >= A AND < B。為了避免漏掉數據,datetime 和 timestamp 使用日期做范圍查詢,可以在后面的日期上加 1 天。
2.原理
2.1 執行器優化
對于 BETWEEN AND 語句,MySQL 優化器會在早期優化時把它優化成等值查詢,所以下面查詢 SQL
SELECT * FROM test3 WHERE id BETWEEN 2 AND 6;在優化器看來完全等價與:
SELECT * FROM test3 WHERE id >= 2 AND id <= 6;兩個 SQL 在性能上沒有差別。
2.2 性能
索引范圍掃描
如果 BETWEEN AND 語句的字段上有索引,則可以使用索引進行范圍掃描,效率很高。我們看一下上面一個 SQL 執行計劃如下:
EXPLAIN SELECT * FROM test3 WHERE id BETWEEN 2 AND 6;
圖片
圖片
type=range,代碼使用到了索引進行范圍掃描,查詢過程如下:
- 存儲引擎從索引樹的根節點開始,找到第一個 id >= 2 的索引條目。這個操作時間復雜度是 O(log n),非常高效;
- 從上面找到的第一個索引條目開始,沿著索引樹的葉子節點向后順序掃描;
- 當遇到第一個 id > 6 的索引條目時,停止掃描;
- 存儲引擎給執行器返回結果集。
全表掃描
如果 BETWEEN AND 語句的字段上沒有索引,只能進行全表掃描,比如下面的語句:
EXPLAIN SELECT * FROM test3 WHERE b BETWEEN 30 AND 50;我們看一下執行計劃:
圖片
圖片
這就需要掃描表中的每一行記錄,逐一判斷字段 b 的值是否落在 [30, 50] 這個區間內。這個執行效率非常低。
非主鍵索引跟等值查詢不同的是,即使 BETWEEN AND 語句的字段加了普通索引,如果查詢的字段用不上覆蓋索引,也會走全表掃描。如下面的語句:
EXPLAIN SELECT * FROM test3 WHERE a BETWEEN 30 AND 50;雖然字段 a 上面有普通索引,但是查詢的是表 test3 中的所有字段,也是走不上 a 這個索引的。我們看一下執行計劃:
圖片
復合索引
我們再看一下復合索引的情況,我們新建一張表,SQL 如下:
CREATE TABLE`test4` (
`id`int(8) NOTNULL AUTO_INCREMENT,
`a`varchar(10) COLLATE utf8_bin DEFAULTNULL,
`b`varchar(10) COLLATE utf8_bin DEFAULTNULL,
`date`dateDEFAULTNULL,
`date_time` datetime DEFAULTNULL,
`time_stamp`timestampNULLDEFAULTNULL,
PRIMARY KEY (`id`),
KEY`idx_a_b` (`a`, `b`)
) ENGINE=InnoDB AUTO_INCREMENT=11DEFAULTCHARSET=utf8 COLLATE=utf8_bin在 test4 這張表上,我們建一個字段(a,b)的復合索引。 BETWEEN AND 語句是可以用最左前綴原則的。如果我們執行下面 SQL:
SELECT a,b,id FROM test4 WHERE a BETWEEN 2 AND 6 AND b = 3;復合索引的第一列 a 使用了范圍查詢,第二列 b 作等值查詢。MySQL 可以利用索引快速找到字段 a 在范圍[2, 6]之間的數據,然后在這些結果中篩選出 b = 3 的記錄。這種情況下字段 a 使用了索引,但是字段 b 沒有用上索引。
圖片
如果我們執行下面 SQL:
SELECT a,b,id FROM test4 WHERE a = 3 and b BETWEEN 2 AND 6;復合索引的第一列 a 使用了等值查詢,第二列 b 做范圍查詢。這種情況也是可以走上索引的。
圖片
如果我們執行下面 SQL:
SELECT a,b,id FROM test4 WHERE b BETWEEN 2 AND 6;這種情況不符合最左前綴原則,所以是走不上索引的。
圖片
索引下推
使用 BETWEEN AND 語句不能走索引下推。
我們再來創建一張表:
CREATE TABLE`test_temp` (
`id`INT(11) NOTNULLDEFAULT'0',
`a`VARCHAR(20) DEFAULTNULL,
`b`VARCHAR(10) DEFAULTNULL,
`c`VARCHAR(10) DEFAULTNULL,
`d`VARCHAR(10) DEFAULTNULL,
PRIMARY KEY (`id`),
KEY`a_b`(`a`,`b`)
) ENGINE=INNODBDEFAULTCHARSET=utf8插入幾條數據
INSERT INTO test_temp VALUES(100, 10, 20, 2, 1);
INSERT INTO test_temp VALUES(200, 10, 40, 4, 2);
INSERT INTO test_temp VALUES(300, 10, 30, 3, 3);
INSERT INTO test_temp VALUES(400, 40, 10, 1, 4);如果我們執行下面的 SQL,是不能走索引下推的:
EXPLAIN SELECT * FROM test_temp WHERE a BETWEEN '10' AND '50' AND b < '50';執行計劃如下:
圖片
圖片
而如果我們把 SQL 改成:
EXPLAIN SELECT * FROM test_temp WHERE a >= '10' AND a <= '50' AND b < '50';執行計劃如下:
圖片
圖片
可以看到,用上了索引下推。
3 總結
本文介紹了 BETWEEN AND 語句的用法、原理和性能,希望對你理解這個范圍查詢語句有所幫助 。































