【SQL BETWEEN子句】掌握范围查询!巧妙指定数值和日期范围的方法
在网站和应用程序开发中,我们经常需要提取某个范围内的数据,比如“价格在1000元以上、5000元以下的商品”或“6月1日至6月30日期间注册的用户”。
在WHERE子句中写这样的条件时,你会怎么做?
WHERE price >= 1000 AND price <= 5000
像这样,使用比较运算符(>=, <=)和AND当然可以指定范围。但是,SQL提供了一个专门的命令,能让这种“范围查询”写得更直观、更简洁。它就是我们今天的主角——`BETWEEN`子句。
本文将通过大量可即拷即用的代码,从BETWEEN子句的基本用法,到在日期范围中的应用,再到其否定形式NOT BETWEEN,进行全面而详尽的解说。让我们告别冗长的比较运算符写法,掌握编写出谁都能看懂的、更智能的SQL的技能吧!
准备工作:创建用于搜索的订单数据
要尝试范围查询,首先需要有可供查询的数据。这次,我们将创建一个模拟电商网站订单历史的orders表。包含各种价格和订单日期的数据,可以让我们更容易地体会到BETWEEN子句的效果。
-- 如果orders表已存在,则删除(以便重复测试)
DROP TABLE IF EXISTS orders;
-- 创建一个新的orders表
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
product_name TEXT NOT NULL,
price INTEGER NOT NULL,
order_date DATE NOT NULL,
quantity INTEGER NOT NULL
);
-- 插入初始数据
INSERT INTO orders (id, product_name, price, order_date, quantity) VALUES
(1, '神奇圆珠笔', 200, '2025-06-05', 3),
(2, '魔法笔记本', 500, '2025-06-10', 5),
(3, '终极电饭煲', 30000, '2025-06-15', 1),
(4, '奇迹苹果', 980, '2025-06-20', 10),
(5, 'USB-C集线器', 4500, '2025-06-25', 2),
(6, '传统毛笔', 12000, '2025-06-30', 1),
(7, '高级巧克力', 3500, '2025-07-01', 4);
这样,用于各种范围数据提取的准备工作就完成了。
【基础】使用`BETWEEN`子句指定数值范围
`BETWEEN`子句的基本语法非常清晰明了。
WHERE 列名 BETWEEN 最小值 AND 最大值;
这里最重要的点是,`BETWEEN`子句的范围包含指定的最小值和最大值本身(即大于等于和小于等于)。也就是说,WHERE price BETWEEN 1000 AND 5000与WHERE price >= 1000 AND price <= 5000的含义完全相同。请务必牢记“包含两端”这一点。
场景:“查找所有价格在1,000元到5,000元之间的商品。”
SELECT * FROM orders
WHERE price BETWEEN 1000 AND 5000;
执行结果:
id | product_name | price | order_date | quantity
---|---------------|-------|------------|----------
5 | USB-C集线器 | 4500 | 2025-06-25 | 2
7 | 高级巧克力 | 3500 | 2025-07-01 | 4
相比使用两个比较运算符,代码更简洁,并且“1,000元到5,000元的范围”这个意图也传达得更直接,不是吗?
【应用】使用`BETWEEN`子句指定日期范围
`BETWEEN`子句的便利之处在于,它不仅能用于数值,也能用于指定日期(DATE类型)和日期时间(DATETIME类型)的范围。在Web应用中,提取特定时间段数据的需求非常多,因此这是一个常用技巧。
场景:“查找所有在2025年6月期间产生的订单。”
在这种情况下,我们将范围的开始指定为“2025-06-01”,结束指定为“2025-06-30”。
SELECT * FROM orders
WHERE order_date BETWEEN '2025-06-01' AND '2025-06-30';
执行结果:
id | product_name | price | order_date | quantity
---|---------------|-------|------------|----------
1 | 神奇圆珠笔 | 200 | 2025-06-05 | 3
2 | 魔法笔记本 | 500 | 2025-06-10 | 5
3 | 终极电饭煲 | 30000 | 2025-06-15 | 1
4 | 奇迹苹果 | 980 | 2025-06-20 | 10
5 | USB-C集线器 | 4500 | 2025-06-25 | 2
6 | 传统毛笔 | 12000 | 2025-06-30 | 1
如你所见,6月1日和6月30日的订单也ちゃんと结果に含まれています。これも「両端を含む」という`BETWEEN`句の性質のおかげです。
【反向条件】使用`NOT BETWEEN`子句指定范围之外的数据
如果你想查找`BETWEEN`的反向条件,即“位于指定范围**之外**的数据”,可以使用NOT BETWEEN子句。
这里有一个注意事项。price BETWEEN 1000 AND 5000的含义是“大于等于1000且小于等于5000”,而price NOT BETWEEN 1000 AND 5000的含义则是“**小于**1000或**大于**5000”(即price < 1000 OR price > 5000)。也就是说,范围的两端值不包含在结果中。
场景:“查找所有价格非常便宜(低于1,000元)或非常昂贵(高于5,000元)的商品。”
SELECT * FROM orders
WHERE price NOT BETWEEN 1000 AND 5000;
执行结果:
id | product_name | price | order_date | quantity
---|---------------|-------|------------|----------
1 | 神奇圆珠笔 | 200 | 2025-06-05 | 3
2 | 魔法笔记本 | 500 | 2025-06-10 | 5
3 | 终极电饭煲 | 30000 | 2025-06-15 | 1
4 | 奇迹苹果 | 980 | 2025-06-20 | 10
6 | 传统毛笔 | 12000 | 2025-06-30 | 1
虽然我们的示例数据中没有价格正好是1,000元或5,000元的商品,但请记住,即使有,它们也不会包含在这个结果中。
【体验环节】在浏览器中运行SQL,掌握范围查询!
现在,是时候把知识变成技能了!将下面的HTML代码完整复制,保存为sql_between_test.html之类的文件名,然后在浏览器中打开它。你专属的SQL执行环境将会启动,并且本文中使用的orders表已经为你准备好了。
试着改变数值或日期的范围,来掌握BETWEEN子句“包含两端”的感觉。也试试NOT BETWEEN,看看结果会如何变化吧!
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL BETWEEN子句 在线执行环境</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.7; color: #333; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
h1 { color: #16a085; }
textarea { width: 100%; height: 180px; font-family: "SF Mono", "Consolas", monospace; font-size: 16px; padding: 12px; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #1abc9c; color: white; border: none; padding: 12px 22px; font-size: 16px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; }
button:hover { background-color: #16a085; }
button:disabled { background-color: #bdc3c7; cursor: not-allowed; }
#result-container { margin-top: 2rem; border: 1px solid #ddd; padding: 1rem; border-radius: 6px; background: #fdfdfd; min-height: 50px; }
#error-message { color: #e74c3c; font-weight: bold; }
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/sql-wasm.js"></script>
</head>
<body>
<h1>来试试SQL吧!</h1>
<p>在下方的文本框中输入SQL语句,然后点击“执行”按钮。试试不同的范围吧!</p>
<textarea id="sql-input">-- 查找6月10日到6月25日之间下单的商品
SELECT * FROM orders
WHERE order_date BETWEEN '2025-06-10' AND '2025-06-25';
</textarea>
<button id="execute-btn">执行</button>
<div id="result-container">
<p id="error-message"></p>
<div id="result-output"></div>
</div>
<script>
const sqlInput = document.getElementById('sql-input');
const executeBtn = document.getElementById('execute-btn');
const errorMsg = document.getElementById('error-message');
const resultOutput = document.getElementById('result-output');
let db;
async function initDb() {
executeBtn.disabled = true;
executeBtn.textContent = '数据库准备中...';
try {
const SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.3/${file}`
});
db = new SQL.Database();
const setupSql = `
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (id INTEGER PRIMARY KEY, product_name TEXT NOT NULL, price INTEGER NOT NULL, order_date DATE NOT NULL, quantity INTEGER NOT NULL);
INSERT INTO orders (id, product_name, price, order_date, quantity) VALUES
(1, '神奇圆珠笔', 200, '2025-06-05', 3),
(2, '魔法笔记本', 500, '2025-06-10', 5),
(3, '终极电饭煲', 30000, '2025-06-15', 1),
(4, '奇迹苹果', 980, '2025-06-20', 10),
(5, 'USB-C集线器', 4500, '2025-06-25', 2),
(6, '传统毛笔', 12000, '2025-06-30', 1),
(7, '高级巧克力', 3500, '2025-07-01', 4);
`;
db.run(setupSql);
executeBtn.disabled = false;
executeBtn.textContent = '执行';
resultOutput.innerHTML = '<p>准备就绪!请随意尝试你自己的SQL查询。</p>';
} catch (err) {
errorMsg.textContent = '数据库初始化失败:' + err.message;
console.error(err);
}
}
function executeSql() {
if (!db) return;
const sql = sqlInput.value;
errorMsg.textContent = '';
resultOutput.innerHTML = '';
try {
const results = db.exec(sql);
if (results.length === 0) {
resultOutput.innerHTML = '<p>查询成功执行,但没有返回结果集。</p>';
return;
}
results.forEach(result => {
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
const headerRow = document.createElement('tr');
result.columns.forEach(colName => {
const th = document.createElement('th');
th.textContent = colName;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
result.values.forEach(row => {
const bodyRow = document.createElement('tr');
row.forEach(cellValue => {
const td = document.createElement('td');
td.textContent = cellValue === null ? 'NULL' : cellValue;
bodyRow.appendChild(td);
});
tbody.appendChild(bodyRow);
});
table.appendChild(thead);
table.appendChild(tbody);
resultOutput.appendChild(table);
});
} catch (err) {
errorMsg.textContent = 'SQL错误:' + err.message;
console.error(err);
}
}
executeBtn.addEventListener('click', executeSql);
initDb();
</script>
</body>
</html>
注意事项:日期时间处理与值的顺序
BETWEEN子句虽然简洁强大,但也有几点需要注意。
- 值的顺序: 在写
BETWEEN A AND B时,必须保证A是较小的值,B是较大的值。如果像BETWEEN 5000 AND 1000这样颠倒顺序,即使数据落在该范围内,也无法匹配到任何记录。请务必养成“最小值 AND 最大值”的书写习惯。 - 日期时间(TIMESTAMP等类型)的处理: 当对包含时间(如`YYYY-MM-DD HH:MM:SS`)的列使用
BETWEEN时,需要特别小心。例如,如果指定order_datetime BETWEEN '2025-06-01' AND '2025-06-30',这在内部实际上被解释为到`'2025-06-30 00:00:00'`为止。因此,6月30日下午(如'2025-06-30 15:00:00')的订单将超出范围,不被包含在结果中。如果想包含整个6月30日,可靠的方法是明确指定时间,如BETWEEN '2025-06-01 00:00:00' AND '2025-06-30 23:59:59',或者使用比较运算符指定为“小于第二天的0点”,如order_datetime >= '2025-06-01' AND order_datetime < '2025-07-01'。
总结
这次,我们学习了能够直观地进行数值和日期范围查询的BETWEEN子句。
- 基本语法:
WHERE 列名 BETWEEN 最小值 AND 最大值。比比较运算符(>= ... AND <= ...)更具可读性。 - 范围包含两端: 这是最重要的点,`BETWEEN`子句的评估方式是“大于等于...且小于等于...”。
- `NOT BETWEEN`: 提取范围之外的数据。它的评估方式是“小于...或大于...”,不包含两端的值。
- 也适用于日期: 通过指定
'YYYY-MM-DD'格式的字符串,可以轻松进行日期范围查询。 - 注意事项: 始终按“最小值 AND 最大值”的顺序书写,并记住在指定包含时间的列的范围时需要一些技巧。
熟练运用BETWEEN子句,你写的SQL将变得更简洁、意图更清晰。从简单的比较运算符组合中更进一步,将更精炼的数据提取技巧加入你的工具箱吧!