☰
MySQL between and 用法详解:闭区间边界、索引匹配与避坑指南
2026/10/16 7:33:50 网站建设 项目流程

1. 项目概述

1.1 核心需求解析

写这篇东西的起因很简单,群里又有人贴了一段带between and的 SQL 过来问为什么查出来的数据不对。翻了下聊天记录,类似的疑问隔三差五就会出现一次。说实话,between and在 MySQL 里属于那种“看着简单,用起来全是细节”的语法,很多人刚接触时以为它就是>=和<=的组合,实际踩过坑之后才发现边界值、数据类型、索引匹配这些问题一个都逃不掉。

这篇文章就把between and的基本用法、底层逻辑和实操经验一次性说清楚,适合刚学 SQL 的新手,也适合写了几年 SQL 但没仔细抠过边界条件的老手。内容主要围绕这几个问题展开:between and到底包不包含边界值、在不同数据类型下表现有什么差异、能不能用好索引、和in、>=这些写法到底怎么选,以及实际开发中最容易翻车的几个场景。

说白了,between and就是一个范围判断语法,核心作用是判断某个值是否落在给定的闭区间内。它解决的是“查某个范围内的数据”这个高频需求,比如按时间范围筛选订单、按价格区间过滤商品、按年龄分段统计用户。能做的事情很明确,但能不能做对,取决于你对它的边界规则和类型转换机制理解到什么程度。

1.2 适用场景与读者群体

先给结论:between and在 MySQL 中的语义是>=和<=的组合,也就是说它包含两端边界值。这一点和很多其他数据库不太一样,比如在某些数据库里between是开区间,但在 MySQL 里请记住,永远是闭区间。

适合看这篇文章的人有三类。第一类是刚入门 SQL 的新手,经常在写范围查询的时候犹豫到底该用between and还是>= and <=,看完能彻底搞清楚两者的等价关系和使用边界。第二类是做数据分析或报表开发的人,天天跟时间范围、数值区间打交道,需要知道怎么写才能既正确又高效。第三类是后端开发工程师,写接口查数据库时经常需要拼动态查询条件,理解between and的索引匹配机制,能帮你避免写出来慢查询自己还不知道。

不管你是哪一类,这篇文章的核心价值就一句话:让你用between and的时候,心里有底。

2. 基础用法与原理解读

2.1 基本语法与执行逻辑

between and的语法非常简洁:

SELECT column1, column2, ... FROM table_name WHERE column_name BETWEEN value1 AND value2;

这个语句的语义是:选取column_name的值大于等于value1且小于等于value2的所有记录。等价写法就是:

WHERE column_name >= value1 AND column_name <= value2

理解这个等价的底层逻辑很重要。MySQL 的优化器在执行between and时,内部就会把它转换成对应的范围扫描条件。也就是说,你在 SQL 里写between and,本质上就是在告诉优化器“我要做一个范围查询”,优化器就会判断是否能走索引,能走的话就进行索引范围扫描。

举一个最基础的例子。假设有一张员工表employee,里面有salary字段,要查询月薪在 8000 到 12000 之间的员工:

SELECT name, department, salary FROM employee WHERE salary BETWEEN 8000 AND 12000;

这个查询会返回所有月薪为 8000、12000 以及两者之间所有薪资档位的员工记录。很多新手在这里会犯嘀咕:8000 和 12000 到底查不查得出来?答案是查得出来,因为 MySQL 的between and是闭区间,两端值都包含在内。

2.2 边界值语义澄清

这里有个特别容易踩坑的点,值得单独拎出来说。between and的边界包含规则在不同数据库里并不统一,但在 MySQL 里非常明确:

提示:MySQL 中BETWEEN value1 AND value2等价于value1 <= column <= value2,包含两个边界值。

实际工作中,我见过不止一次因为边界理解错误导致线上数据对不上的情况。最常见的是按日期范围统计报表,比如要统计某月 1 号到 15 号的数据:

SELECT COUNT(*) FROM orders WHERE order_date BETWEEN '2024-11-01' AND '2024-11-15';

如果order_date是DATE类型,这条 SQL 统计的就是 11 月 1 日 0 点 0 分 0 秒到 11 月 15 日 0 点 0 分 0 秒的数据。也就是说,11 月 15 日当天的数据,只有精确到零点那一刻的才会被包含。如果你期望的是包含 11 月 15 日一整天,这么写就漏数据了。

这是between and在日期处理上最经典的陷阱。很多人以为传一个日期字符串进去,MySQL 会自动理解成“包含当天全天”,其实不是。它会按你传入的字符串解析,落在时间戳比较的层面就是那一个瞬间。

2.3 数值型与日期型的差异表现

先看数值型。between and用于整数、小数、浮点数时,表现最符合直觉,就是纯粹的大小比较,不存在任何歧义。比如查询价格在 9.9 到 199.9 之间的商品:

SELECT product_name, price FROM products WHERE price BETWEEN 9.9 AND 199.9;

这里 9.9 和 199.9 都会被包含,查询结果包含所有价格在这两个数字之间的商品记录。因为数值比较没有类型转换的歧义,所以这个场景基本不会出问题。

再看日期时间型。这个就要仔细了。MySQL 的日期时间类型有DATE、DATETIME、TIMESTAMP等。当字段是DATETIME类型时,用between and传字符串日期,会按照YYYY-MM-DD HH:MM:SS的格式进行解析和比较。如果你只传'2024-11-15',MySQL 会把它当作'2024-11-15 00:00:00'来处理,这就导致 11 月 15 日当天大部分时间的数据都被排除在外。

字符串类型字段用between and也要格外小心。如果字段是VARCHAR类型,里面存的是'2024-11-01'这样的字符串,between and比较的就是字符串的字典序,而不是日期的大小。虽然 ISO 格式的日期字符串按字典序排就是按时间顺序排,但一旦格式不统一,比如有的存'2024/11/01',有的存'2024-11-01',结果就会乱套。我后面会专门讲这个问题。

3. 核心实操场景拆解

3.1 数值范围查询的标准写法

数值范围查询是between and最典型的应用场景,没有之一。不管是电商系统的价格区间筛选、金融系统的金额范围统计,还是 HR 系统的薪资区间查询,只要字段是数值类型,写法都是固定的套路。

看一个完整的例子。假设有一张订单表orders,包含order_id、customer_id、order_amount、order_date字段。要查询订单金额在 100 到 500 元之间的所有订单:

SELECT order_id, customer_id, order_amount, order_date FROM orders WHERE order_amount BETWEEN 100 AND 500 ORDER BY order_amount DESC;

执行结果会返回金额从 100 元到 500 元(含两端)的所有订单,并按金额降序排列。如果你想去掉边界值,只查严格大于 100 且严格小于 500 的订单,那就不能直接用between and了,得改成:

SELECT order_id, customer_id, order_amount, order_date FROM orders WHERE order_amount > 100 AND order_amount < 500 ORDER BY order_amount DESC;

这里要理解为什么不能用between and加排除条件来实现。表面上看,你可以在where后面同时写order_amount BETWEEN 100 AND 500 AND order_amount != 100 AND order_amount != 500,也能达到效果,但这样写既啰嗦又容易遗漏边界条件。更合理的做法是直接放弃between and,改写为两个严格比较,语义一目了然。

还有一种常见需求是按整数区间分组统计,比如统计不同年龄段的人数:

SELECT CASE WHEN age BETWEEN 18 AND 24 THEN '18-24岁' WHEN age BETWEEN 25 AND 34 THEN '25-34岁' WHEN age BETWEEN 35 AND 44 THEN '35-44岁' ELSE '其他' END AS age_group, COUNT(*) AS user_count FROM users GROUP BY age_group;

这个写法在报表开发中非常常用。但要注意分组边界不能重叠,否则数据会被重复统计。比如上例中 24 和 25 是相邻的,没有重叠,但如果写成18-24和24-30,那 24 岁的人就会被两个区间同时包含,统计结果就错了。

3.2 时间范围查询的高频坑点

时间范围查询是between and使用频率最高、出错率也最高的场景,值得单独花一大节来讲。

先明确一个概念:MySQL 中日期时间字段的比较,本质上是时间戳或时间值的比较。当你在where条件中写order_date BETWEEN '2024-11-01' AND '2024-11-30',如果order_date是DATETIME类型,MySQL 会把这个条件理解为:

order_date >= '2024-11-01 00:00:00' AND order_date <= '2024-11-30 00:00:00'

问题来了:11 月 30 日零点以后的数据全部被排除。也就是说,11 月 30 日当天产生的所有订单,除了恰好发生在零点整的那一条,其他都不会出现在查询结果里。这是报表数据对不上账的头号原因。

解决这个问题的方案有两种。

第一种,把结束日期加上一天,然后用开区间:

SELECT order_id, order_amount FROM orders WHERE order_date >= '2024-11-01' AND order_date < '2024-12-01';

这种写法在业界最为推荐。它既包含了 11 月 1 日零点开始的所有数据,又包含了 11 月 30 日全天数据,因为order_date < '2024-12-01'等价于order_date <= '2024-11-30 23:59:59.999...'。而且这个写法对索引的使用更友好,不会因为微秒精度问题漏掉最后一条记录。

第二种,如果是按固定天数,比如查最近 7 天的数据,可以直接用时间函数:

SELECT order_id, order_amount FROM orders WHERE order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE();

但注意,这个写法同样存在边界问题。CURDATE()返回的是当天零点,所以这会包含最近 7 天零点的所有数据,但最后一天只有零点那一刻的数据。如果是业务上要查“最近 7 天(含今天凌晨至今)”,那要明确知道这个写法的口径是什么。如果是要包含今天一整天,那还是得用>=加<的组合,或者直接把结束时间改成DATE_ADD(CURDATE(), INTERVAL 1 DAY)。

还有一种是按周或按月统计的经典写法。比如查第 45 周的订单,可以先算出这周的周一和周日日期:

SELECT order_id, order_amount FROM orders WHERE order_date BETWEEN '2024-11-04' AND '2024-11-10';

如果order_date是DATETIME类型,这个 SQL 会漏掉 11 月 10 日 0 点以后的订单。正确写法应该是:

WHERE order_date >= '2024-11-04' AND order_date < '2024-11-11';

总结成一句话:日期时间字段做范围查询,能用>=和<就不要用between and,除非你能保证结束日期那一整天都不要。

3.3 字符类型与字符串日期处理

当字段是字符串类型时,between and的行为就更微妙了。

假设有一个表logs,log_date字段是VARCHAR(10),存储格式为'2024-11-01'。直接用between and查询:

SELECT * FROM logs WHERE log_date BETWEEN '2024-11-01' AND '2024-11-30';

在格式统一的前提下,这个查询是可用的,因为 ISO 8601 格式的日期字符串字典序就是时间顺序。'2024-11-30'在字典序上确实大于'2024-11-01'且小于'2024-12-01',所以范围比较是符合预期的。

但这里有一个互联网开发中比较常见的问题:字符串日期格式混用。比如有的业务会把日期存成'2024/11/01'或'2024.11.01',这时候用between and就是灾难。因为'2024/11/30'和'2024/11/01'的字典序比较,跟日期顺序根本不是一回事。

避免这个问题的唯一可靠办法是:查询之前先用STR_TO_DATE或DATE_FORMAT统一格式,或者干脆不要用字符串存日期,改用DATE或DATETIME类型。都 2024 年了,真的不建议再为了省事用字符串存时间。

话说回来,如果你的log_date字段是真正的DATE或DATETIME类型,那就没有这些破事。类型对了,between and的语义才稳定可靠。

4. 进阶用法与索引性能分析

4.1 与 IN、>= <= 的等价性与区别

很多人在写范围查询时会纠结到底用between and、in、>= and <=哪个。实际上,这三者适用的场景不同,不能简单画等号。

between and和>= and <=在语义上是完全等价的,两者在 MySQL 优化器看来几乎是同一个东西,生成的执行计划也基本一致。如果字段上有索引,两者都会走索引范围扫描。所以从功能和技术性能的角度看,选哪个纯看个人偏好。我个人的习惯是:字段是数值或日期时间类型时,用between and,简洁;涉及边界条件需要额外调整时,改用>=和<。

in列表和between and的使用场景有明显不同。in适合“离散的几个值”,比如查部门编号为 1、3、5 的员工;between and适合“连续的范围”,比如查薪资 8000 到 12000。如果把in用在一个连续的、值的数量很多的范围内,比如id IN (1, 2, 3, ..., 1000),SQL 会变得冗长且难以维护,而用between and或>= <=两行就能搞定。

有一个特殊情况值得注意:如果in列表里的元素是连续的,比如id IN (1, 2, 3, 4, 5),优化器有时会将其优化成范围查询id >= 1 AND id <= 5,但也可能不优化。不同版本、不同数据分布下行为不一定一样,所以别依赖这种隐式优化,写清楚意图最好。

还有一种场景是用between and做分页或数据切片。比如一次性查 100 万条数据压力太大,可以按 ID 范围切段:

SELECT * FROM big_table WHERE id BETWEEN 1 AND 100000; SELECT * FROM big_table WHERE id BETWEEN 100001 AND 200000;

这种分段查询在大数据量的离线任务中非常有用。因为主键索引是有序的,按 ID 范围切片可以避免全表扫描,每个切片走的都是主键范围扫描,性能稳定。

4.2 索引利用与性能优化

between and能不能用上索引,取决于字段类型和查询条件的写法。这是一个可以展开说很多的重要内容。

对数值类型字段,比如age、salary、price,通过between and进行范围查询,MySQL 优化器会非常积极地去走索引。因为范围查询是 B+ 树索引最擅长的场景之一,索引扫描的效率远高于全表扫描。看执行计划的话,EXPLAIN显示type = range,说明走的是索引范围扫描,这是比较理想的情况。

对日期时间类型字段,between and同样能很好地利用索引。只要你把字段类型定义正确,索引列上做范围比较,优化器就能命中索引。这就是为什么前文不建议用字符串存日期的另一个重要原因:字符串类型的日期字段,即使建了索引,范围查询时也可能因为隐式类型转换或排序规则问题导致索引失效。

索引失效最常见的原因之一是隐式类型转换。举个我在实际开发中遇到的例子:某表的user_id字段是VARCHAR类型,但传入参数是数字:

SELECT * FROM users WHERE user_id BETWEEN 10001 AND 20000;

这种情况下,MySQL 会把user_id字符串隐式转换为数字再比较,导致字段上的索引无法正常使用,执行计划会变成全表扫描。正确做法是让参数类型和字段类型保持一致:

SELECT * FROM users WHERE user_id BETWEEN '10001' AND '20000';

如果你不确定字段是什么类型,可以用DESC命令查看表结构,或者用SHOW CREATE TABLE确认字段定义。这是排查索引是否失效的第一步。

还有一个细节是关于索引前缀的。如果字段上建的是联合索引,比如INDEX idx_department_salary (department, salary),那么按salary BETWEEN ...单独查询时,只能用到联合索引的前缀部分,效率可能不如为salary单独建一个索引。所以在设计表结构时,要考虑实际查询条件中字段的使用顺序,把最常用的等值条件字段放在联合索引最前面,范围条件字段尽量往后放。这是索引设计的基本原则,和between and本身没直接关系,但会影响最终查询速度。

4.3 与 NULL 值的交互行为

between and处理NULL值时,有一个很多新手不太注意的规则。

在 SQL 中,NULL代表“不知道”或“无值”,任何与NULL的比较结果都是“未知”,不会返回TRUE。用between and做范围判断也一样,如果某行的字段值是NULL,那它无论如何都不会出现在between and的查询结果里。

举个例子,表中有一条员工的salary字段是NULL,执行:

SELECT * FROM employee WHERE salary BETWEEN 8000 AND 12000;

这条记录不会出现在结果中,因为8000 <= NULL的比较结果是未知,NULL <= 12000的结果也是未知,AND连接的两个未知条件结果还是未知,最终被过滤掉。这在大多数业务场景下是合理的,因为薪资未知的员工本来就不该被统计进薪资区间。

但如果你确实需要把薪资未知的员工也列出来,那就得额外加条件:

SELECT * FROM employee WHERE salary BETWEEN 8000 AND 12000 OR salary IS NULL;

需要注意的是,OR的使用可能会影响索引的选择。如果前面的范围条件能走索引,加上OR salary IS NULL后,优化器可能需要做索引合并或者干脆走全表扫描。数据量大的时候这种查询性能可能会明显下降。解决办法是把查询拆成两个,用UNION ALL合并:

SELECT * FROM employee WHERE salary BETWEEN 8000 AND 12000 UNION ALL SELECT * FROM employee WHERE salary IS NULL;

这样两个查询各自都能充分利用索引,性能更可控。当然,拆成两条 SQL 在业务代码里执行也可以,效果是一样的。

5. 常见问题与排查技巧实录

5.1 排查方向与常见误区

实际写代码的时候,between and出问题通常就那几种情况,我整理成了一个速查表,方便直接对照。

问题现象可能原因解决方案
查询结果少了最后一天的数据DATETIME字段用between传日期字符串,结束边界被解析为当天零点改用>=和< 下一天写法
字符串字段查询结果顺序奇怪字段是VARCHAR,按字典序比较而非日期序统一字符串格式或改字段为DATE类型
查询很慢隐式类型转换导致索引失效检查字段类型与参数类型是否一致
数值字段用了between却查出 NULL 相关记录对NULL的比较结果始终为未知如需包含 NULL,额外加OR field IS NULL
报表数与明细对不上多个统计口径混用,边界理解不一致统一使用>=和<标准写法
用between查出的数量比预期多边界值重复统计(比如分组区间重叠)检查分组边界是否重叠

排查时第一步永远是确认字段类型。用DESC table_name;查看表结构,明确字段是INT、DECIMAL、DATE、DATETIME还是VARCHAR。不同类型下between and的边界行为和比较规则完全不同。

第二步是确认传入参数的类型是否与字段一致。如果是写代码拼接 SQL,注意参数是用字符串还是数字传入的。框架层面很多时候会自动做类型绑定,但如果你是在数据库客户端手动执行 SQL,要特别小心参数写法。

第三步是看执行计划。在SELECT前加EXPLAIN,检查type字段是否为range,key字段是否是你预期使用的索引。如果type = ALL,基本可以判定是全表扫描,这时候要回头看是不是类型转换或索引缺失导致。

5.2 几个经典坑的完整复盘

第一个坑是日期统计漏数。有一次做月度报表,统计 11 月份订单总额,用了:

SELECT SUM(order_amount) FROM orders WHERE order_date BETWEEN '2024-11-01' AND '2024-11-30';

结果月底对账时发现总额比明细少了。排查了半天才发现问题出在order_date是DATETIME类型,'2024-11-30'被解析为2024-11-30 00:00:00,11 月 30 日当天 0 点以后的订单全部没统计进来。后来改成:

WHERE order_date >= '2024-11-01' AND order_date < '2024-12-01';

问题立刻解决。这是个非常低级的错误,但因为它不影响“大部分数据”,只是影响最后一天,所以很容易被忽略。每次做统计报表,我都会提醒自己检查结束边界。

第二个坑是字符串类型字段的字典序查询。某系统历史表里有一个stat_date字段,最初设计为VARCHAR(8),存'20241101'这样的格式。后来发现程序里又有传'2024-11-01'格式的,两边混着用,between and的结果就随缘了。最终统一了格式规范,并把新表的字段改成了DATE类型,才从根本上解决。

第三个坑是隐式类型转换导致索引失效。一张千万级用户表,mobile字段是VARCHAR(20),查询时用mobile BETWEEN 13800000000 AND 13999999999。这条 SQL 执行了 2 秒多,加上EXPLAIN一看,type = ALL,全表扫描。把参数改成字符串'13800000000'、'13999999999'后,查询时间降到几十毫秒。这个坑在写接口时特别容易踩,因为框架传参时经常会把数字参数解析成整数类型。

5.3 实操心得与避坑清单

根据前面这些实操经验,我总结了几条属于“必须刻在脑子里”的规则。

第一,能用>= 和 <就不要用between and做日期时间范围的结束边界处理。这不是说between and不好,而是它的闭区间语义在时间查询上天然容易漏掉最后一天的数据。如果你坚持要用,务必记得把结束日期加一天。

第二,字符串字段的between and按字典序比较。虽然 ISO 格式的日期字符串恰好字典序等于时间序,但日期时间字符串就不是了。'2024-11-30 09:00:00'和'2024-11-30 18:00:00'谁大谁小,取决于字符位置上的值,这个规律很难直观判断,所以字符串字段尽量别存时间。

第三,遇到慢查询先看执行计划。不要凭感觉怀疑是数据量太大,很多时候就是索引没走。加上EXPLAIN看type和key两列,问题基本就暴露了。如果type = range,说明索引范围扫描,性能没问题;如果是ALL,说明全表扫描,必须排查原因。

第四,分组统计时检查边界是否重叠。用between and分年龄段、价格段时,一定要确认每个区间的边界是连续的但不重叠。比如18-24、25-34是连续的,不会重复统计;但18-24、24-30就重叠了,24 岁的人会被统计两次。

第五,注意时区问题。MySQL 的TIMESTAMP类型存储时会根据数据库时区转换,不同时区下同一个时间字段的值可能不同,但这不影响between and本身的边界语义,只是你传的边界字符串要确保在正确的时区下解释。涉及跨时区业务时,建议统一用 UTC 存储,展示时再转换。

第六,动态 SQL 中要小心边界参数为空的情况。很多后端的查询接口会接收可选的起止参数,比如minPrice和maxPrice。如果只传了minPrice,动态 SQL 拼出来的是price BETWEEN 100 AND NULL,这种情况等于没查。正确处理方式是分别判断参数是否为空,动态拼接>=或<=条件,而不是直接套between and。

6. 实战案例与扩展应用

6.1 典型查询场景的完整 SQL 示例

复合条件查询,比如按时间段和金额区间同时筛选订单:

SELECT order_id, customer_id, order_amount, order_date FROM orders WHERE order_date >= '2024-11-01' AND order_date < '2024-12-01' AND order_amount BETWEEN 500 AND 2000 ORDER BY order_amount DESC, order_date ASC;

这个查询里既有日期范围(用>=和<避免漏数),又有金额范围(用between and),两个条件都是范围条件。如果表上有(order_date, order_amount)的联合索引,这个查询会先用日期范围过滤,再在日期范围内用金额做二级筛选,效率比较理想。

按月度连续统计,用GROUP BY配合日期聚合:

SELECT DATE_FORMAT(order_date, '%Y-%m') AS month, COUNT(*) AS order_count, SUM(order_amount) AS total_amount FROM orders WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01' GROUP BY DATE_FORMAT(order_date, '%Y-%m') ORDER BY month;

配合between and的分区统计,比如按价格区间统计商品数量:

SELECT SUM(price BETWEEN 0 AND 99) AS price_0_99, SUM(price BETWEEN 100 AND 499) AS price_100_499, SUM(price BETWEEN 500 AND 999) AS price_500_999, SUM(price >= 1000) AS price_1000_plus FROM products;

这里用了SUM(condition)的技巧,条件成立返回 1,不成立返回 0,用求和实现计数,一条 SQL 完成多个区间的统计。这种写法在报表开发中很常用,比写多个COUNT子查询清晰得多。

6.2 分区表与分页场景的配合使用

在大数据量场景下,between and经常和表分区配合使用。比如按月分区的订单表,查询某月数据时会做分区裁剪,只扫描对应分区,效率很高。

SELECT order_id, order_amount FROM orders_partitioned WHERE order_date >= '2024-11-01' AND order_date < '2024-12-01';

分区裁剪的原理是:MySQL 根据查询条件中的分区键范围,直接跳过不相关的分区。如果你的分区字段是order_date,并且按RANGE分区按月划分,那上面的查询只扫描 11 月那个分区,其他分区完全不会碰。这时候日期范围条件用>=和<是必要的,因为分区边界通常定义成LESS THAN的形式,>和<才能精确对应分区边界。

如果在这里用between and写order_date BETWEEN '2024-11-01' AND '2024-11-30',虽然也能触发分区裁剪,但边界条件和分区边界的匹配不如>= 和 <直观,而且存在漏掉 11 月 30 日当天数据的隐患。所以分区表上的范围查询,我更推荐统一的>=和<风格。

分页场景中,between and可以用来做基于 ID 的键集分页,也就是“用上一页最后一条记录的 ID 作为下一页起始条件”。这种方式比传统的LIMIT OFFSET在深分页时性能好很多:

SELECT id, user_name, created_at FROM users WHERE id > 100000 -- 上一页最后一条记录的 ID ORDER BY id ASC LIMIT 100;

虽然这没有直接用between and,但底层思路是一样的:基于有序主键的范围扫描。如果你要取一批固定 ID 范围内的数据,比如 ID 在 1 到 5 万之间的用户,直接用between and就行,一走主键范围扫描,效率非常稳定。数据量从几万到几百万时这种写法性能差异不大,因为主键索引的有序性保证了范围扫描的高效。

6.3 与其他功能的组合:JOIN、子查询、CASE

between and和JOIN配合最常见的场景是“按区间关联”。比如一张价格区间配置表price_bands,每行定义了一个区间和对应的折扣,然后关联订单表计算折扣价:

SELECT o.order_id, o.order_amount, b.discount_rate, o.order_amount * b.discount_rate AS discounted_amount FROM orders o JOIN price_bands b ON o.order_amount BETWEEN b.min_amount AND b.max_amount;

这个关联条件用的是非等值 JOIN,MySQL 会怎么执行取决于数据量。如果区间表很小,优化器可能会先加载区间表到内存做嵌套循环匹配;如果两张表都大,这个关联的代价会非常高。实际开发中要评估区间表的规模和订单表的规模,必要时改写成CASE条件判断或用报表工具预处理。

between and在CASE表达式中很常见,前文已经有类似例子。再补充一个动态区间的场景:如果区间阈值在另一个表里,需要先算出区间再查询,这种就不适合 SQL 里硬编码数值,而是先查询出阈值,再拼进 SQL。比如查业绩在“平均线到平均线 1.2 倍”之间的员工,可以先执行子查询:

SELECT name, performance_score FROM employees WHERE performance_score BETWEEN (SELECT AVG(performance_score) * 1.0 FROM employees) AND (SELECT AVG(performance_score) * 1.2 FROM employees);

子查询里用聚合函数和表达式作为between and的边界值,MySQL 是支持的。但要注意,如果子查询结果不是标量,比如返回多行,这个写法会报错。所以必须保证子查询用聚合函数且没有GROUP BY,返回一行一列。

我个人的经验是:别把between and的边界写得太过复杂。边界值是常量最清晰,写子查询或表达式虽然语法支持,但可读性和可维护性都会下降。如果想清楚表达业务逻辑,可以先单独算出边界值,再作为参数传入查询。这样 SQL 既简洁,排查问题时也能直接看到数值,省去心算过程。

7. 总结与个人体会

写到这里,between and的核心内容基本都覆盖了。最后聊几句我个人在实际操作中的体会。

between and是一个简单但需要敬畏的语法。它的基本语义清晰明了,闭区间判断、支持数值和日期时间、能走索引范围扫描,这些都是它的优点。但它的边界陷阱也同样突出,尤其是日期时间的处理上,一个不小心就是数据对不上。

我个人在写查询 SQL 时形成了这样的习惯:数值型范围用between and,因为它简洁、直观,不会产生歧义;日期时间型范围用>=和<,因为闭区间的结束边界天然存在漏数风险。这样统一之后,团队协作时每个人对 SQL 的口径都能快速达成一致,不需要每次重新讨论边界语义。

另外一个很深的体会是:理解between and的底层机制,比死记硬背语法更重要。为什么字符串字段按字典序比较?为什么隐式类型转换会破坏索引?为什么闭区间在日期上会漏数?这些问题本质上都指向同一个核心:SQL 查询的结果取决于字段类型、数据存储方式和比较规则的共同作用。把这些底层机制弄明白了,遇到任何边界 case 都能举一反三,而不是靠背答案。

最后再分享一个小技巧:写完涉及范围查询的 SQL,我习惯先拿边界值本身做一次验证。比如查询条件是salary BETWEEN 8000 AND 12000,我就在同一批数据里查一遍WHERE salary = 8000和WHERE salary = 12000,看看这两条记录是否出现在结果里。这个验证只需要几秒钟,但能帮你及早发现边界条件的问题,避免报表上线之后才发现数据对不上,到时候排查成本就高了。

between and不是什么高深技术,但它确实是日常开发中用到的频率最高、最容易在细节上翻车的语法之一。把这篇文章里提到的边界规则、类型差异、索引原理和排查方法吃透,你在写范围查询的时候就能少踩很多坑。以后不管遇到数值区间、日期范围还是字符串匹配,心里都有一本清晰的账。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询