牛客 SQL8要求查询 20 岁及以上、23 岁及以下的用户,返回设备 ID、性别、年龄。
原题要求的是闭区间,两端都包含:
SELECT device_id, gender, age FROM user_profile WHERE age >= 20 AND age <= 23;原稿用 && 连接带引号的数字。在 MySQL 原环境中不必然错,这次改用标准 AND 和数值常量,让条件更清楚。本篇接着单个年龄边界,重点看“两个边界怎样围成一个范围”。
1. 两个条件是取交集,不是取并集
至少20岁: 20 ------------------> 最多23岁: <------------------ 23 同时满足: 20 ======== 23如果换成 OR,只要满足一个条件就能进入。19 岁满足“不超过 23”,24 岁满足“不低于 20”,两个本来应该排除的年龄都被收进来了。对于已知数值,age >= 20 OR age <= 23 覆盖整个数轴,不是年龄段。
2. BETWEEN 是含两端的简写
对本题数值类型一致的年龄与常量,可以写成:
SELECT device_id, gender, age FROM user_profile WHERE age BETWEEN 20 AND 23;20 和 23 都会被包含。这里的 AND 属于 BETWEEN 语法,不是要把 BETWEEN 20 当成独立条件。MySQL BETWEEN 说明。
不能把 BETWEEN 23 AND 20 理解为数据库自动交换上下限。若接口传来两个边界,应该先验证顺序,必要时按业务规则规范化,而不是期待 SQL 猜意图。
3. 放齐端点内外的数据再比较
在自己的练习数据库建立独立表,表名存在时换名:
CREATE TABLE sql8_range_demo ( device_id INT NOT NULL PRIMARY KEY, gender VARCHAR(14) NOT NULL, age INT ); INSERT INTO sql8_range_demo VALUES (1,'male',19),(2,'male',20),(3,'female',21), (4,'female',23),(5,'male',24),(6,'male',NULL);SELECT device_id, gender, age FROM sql8_range_demo WHERE age BETWEEN 20 AND 23 ORDER BY device_id;| device_id | gender | age |
|---|---|---|
| 2 | male | 20 |
| 3 | female | 21 |
| 4 | female | 23 |
加 ORDER BY 只是让实验输出便于核对,不表示 BETWEEN 会自动排序。
可以把几种条件一次取出来对照:
SELECT device_id, age, (age >= 20 AND age <= 23) AS closed_range, (age BETWEEN 20 AND 23) AS between_range, (age >= 20 OR age <= 23) AS wrong_union FROM sql8_range_demo ORDER BY device_id;| age | closed_range | between_range | wrong_union |
|---|---|---|---|
| 19 | 0 | 0 | 1 |
| 20 | 1 | 1 | 1 |
| 21 | 1 | 1 | 1 |
| 23 | 1 | 1 | 1 |
| 24 | 0 | 0 | 1 |
| NULL | NULL | NULL | NULL |
NULL 不是 0 岁。区间比较结果未知,WHERE 不保留它,不能由没有出现在结果里推断这个用户年龄一定在范围之外。
4. 同一个写法,不能机械照搬到时间筛选
如果年龄需求改为“大于 20,且小于 23”,就应该改成 > 与 <,不是继续沿用含两端的 BETWEEN。
时间字段还有另一个常见问题:“查某一天”不是“查某天零点到某天零点”。下面用独立名字 logs_demo 作 SQL 示意,假设它有 created_at 时间列,未提供该表就不能直接执行:
SELECT created_at FROM logs_demo WHERE created_at >= '2026-10-05 00:00:00' AND created_at < '2026-10-06 00:00:00';这是左闭右开区间,覆盖起点而不包含次日零点,避免靠猜测“当天最后一秒”去补边界。实际使用还要确认时间列类型、精度与时区,本次没有运行这个 MySQL 时间实验,不把它当作年龄表的实测结果。
5. 验证与收获
本次在 SQLite 内存数据库运行年龄实验,核对输出三列、AND 与 BETWEEN 的一致性、两端保留、范围外排除、NULL 与空表。还测试交换上下限,并用错误 OR、漏掉等号的开区间作为反例,确认测试能抓到它们。
MySQL BETWEEN 语义按官方文档核对;不由 SQLite 的类型转换结果推断所有 MySQL 字符串或时间比较。条件写法是否更快,也没有在本次测量。
区间题的第一步不是挑哪个关键字,而是画出边界:端点是否包含?两个条件要交集还是并集?未知值怎么处理?先把这几个问题写清楚,SQL 才是在准确表达范围,而不是拼出一句能执行的语句。