欢迎来到MySQL系列教程的第4天!今天我们将学习条件查询和排序,这是SQL中最常用的操作。掌握了WHERE、ORDER BY和LIMIT,你就能从海量数据中精准找到需要的信息。让我们开始吧!

文章目录
-
- 一、写在前面
- 二、WHERE子句详解
-
- [2.1 比较运算符](#2.1 比较运算符)
- [2.2 逻辑运算符](#2.2 逻辑运算符)
- [2.3 IN和NOT IN](#2.3 IN和NOT IN)
- [2.4 BETWEEN范围查询](#2.4 BETWEEN范围查询)
- [2.5 LIKE模糊查询](#2.5 LIKE模糊查询)
- 三、NULL值处理
-
- [3.1 IS NULL vs = NULL](#3.1 IS NULL vs = NULL)
- [3.2 NULL相关函数](#3.2 NULL相关函数)
- [3.3 NULL值排序](#3.3 NULL值排序)
- [四、ORDER BY排序](#四、ORDER BY排序)
-
- [4.1 单列排序](#4.1 单列排序)
- [4.2 多列排序](#4.2 多列排序)
- [4.3 FIELD自定义排序](#4.3 FIELD自定义排序)
- [4.4 表达式排序](#4.4 表达式排序)
- 五、LIMIT分页
-
- [5.1 基础分页](#5.1 基础分页)
- [5.2 带排序的分页](#5.2 带排序的分页)
- [5.3 分页公式](#5.3 分页公式)
- 六、实战:电商系统的各种查询场景
-
- [6.1 用户相关查询](#6.1 用户相关查询)
- [6.2 商品相关查询](#6.2 商品相关查询)
- [6.3 订单相关查询](#6.3 订单相关查询)
- [6.4 综合查询](#6.4 综合查询)
- 七、踩坑提醒
-
- [7.1 LIKE '%xxx%'导致索引失效](#7.1 LIKE '%xxx%'导致索引失效)
- [7.2 类型转换导致索引失效](#7.2 类型转换导致索引失效)
- [7.3 OR条件导致索引失效](#7.3 OR条件导致索引失效)
- 八、面试高频考点
-
- 考点1:WHERE和HAVING的区别?
- [考点2:LIMIT 1000000,10的性能问题?](#考点2:LIMIT 1000000,10的性能问题?)
- [考点3:如何优化ORDER BY?](#考点3:如何优化ORDER BY?)
- [考点4:IS NULL和= NULL的区别?](#考点4:IS NULL和= NULL的区别?)
- 九、总结
- 十、下一步预告
- 十一、参考资料
- 互动话题
一、写在前面
在实际工作中,我们很少直接查询全表数据,而是需要根据各种条件筛选。今天的内容包括:
- WHERE子句:精确筛选数据
- NULL值处理:避免常见陷阱
- ORDER BY排序:让数据有序呈现
- LIMIT分页:大数据量的分页显示
二、WHERE子句详解
WHERE子句用于筛选满足条件的记录,是SELECT语句中最常用的子句。
2.1 比较运算符
sql
-- 等于 =
SELECT * FROM users WHERE status = 1;
-- 不等于 != 或 <>
SELECT * FROM users WHERE status != 0;
SELECT * FROM users WHERE status <> 0;
-- 大于、小于
SELECT * FROM products WHERE price > 5000;
SELECT * FROM products WHERE stock < 10;
-- 大于等于、小于等于
SELECT * FROM orders WHERE total_amount >= 10000;
SELECT * FROM products WHERE price <= 1000;
-- 范围查询(等同于 >= AND <=)
SELECT * FROM products WHERE price BETWEEN 1000 AND 5000;
2.2 逻辑运算符
sql
-- AND:同时满足多个条件
SELECT * FROM products
WHERE price > 1000 AND stock > 0 AND status = 1;
-- OR:满足任一条件
SELECT * FROM products
WHERE category = '手机' OR category = '电脑';
-- NOT:取反
SELECT * FROM users WHERE NOT status = 0;
-- 混合使用(注意优先级:NOT > AND > OR)
SELECT * FROM products
WHERE (category = '手机' OR category = '电脑')
AND price < 10000
AND status = 1;
2.3 IN和NOT IN
sql
-- IN:在指定集合中
SELECT * FROM products WHERE category IN ('手机', '电脑', '平板');
-- 等同于:
SELECT * FROM products
WHERE category = '手机' OR category = '电脑' OR category = '平板';
-- NOT IN:不在指定集合中
SELECT * FROM orders WHERE status NOT IN (3, 4);
-- 查询未完成的订单(排除已完成和已取消)
-- 子查询配合IN
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE total_amount > 10000);
2.4 BETWEEN范围查询
sql
-- 数值范围
SELECT * FROM products WHERE price BETWEEN 1000 AND 5000;
-- 日期范围
SELECT * FROM orders
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31';
-- 注意:BETWEEN包含边界值
-- 等同于:
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at <= '2024-01-31';
-- NOT BETWEEN
SELECT * FROM products WHERE price NOT BETWEEN 100 AND 1000;
2.5 LIKE模糊查询
sql
-- % 匹配任意多个字符
SELECT * FROM products WHERE name LIKE '%iPhone%'; -- 包含iPhone
SELECT * FROM users WHERE username LIKE 'zhang%'; -- 以zhang开头
SELECT * FROM users WHERE email LIKE '%@gmail.com'; -- 以@gmail.com结尾
-- _ 匹配单个字符
SELECT * FROM products WHERE name LIKE 'iPhone _'; -- iPhone + 一个字符
SELECT * FROM users WHERE username LIKE 'user00_'; -- user001, user002等
-- 转义特殊字符
SELECT * FROM products WHERE name LIKE '%\%%'; -- 包含%符号
SELECT * FROM products WHERE name LIKE '%\_%'; -- 包含_符号
三、NULL值处理
3.1 IS NULL vs = NULL
这是MySQL中最常见的错误之一!
sql
-- 错误!= NULL 永远返回空结果
SELECT * FROM users WHERE phone = NULL; -- 错误!
-- 正确:使用 IS NULL
SELECT * FROM users WHERE phone IS NULL;
-- 正确:使用 IS NOT NULL
SELECT * FROM users WHERE phone IS NOT NULL;
原因: NULL表示"未知",任何与NULL的比较结果都是NULL(假),必须使用IS NULL或IS NOT NULL。
3.2 NULL相关函数
sql
-- IFNULL:如果为NULL则返回默认值
SELECT
username,
IFNULL(phone, '未绑定') AS phone_display
FROM users;
-- COALESCE:返回第一个非NULL值
SELECT
username,
COALESCE(phone, email, '无联系方式') AS contact
FROM users;
-- NULLIF:如果相等则返回NULL
SELECT NULLIF(1, 1); -- 返回NULL
SELECT NULLIF(1, 2); -- 返回1
3.3 NULL值排序
sql
-- MySQL中NULL默认排在最前面(升序)
SELECT * FROM users ORDER BY phone; -- NULL在前
-- 让NULL排在最后
SELECT * FROM users ORDER BY phone IS NULL, phone;
-- 或者使用IFNULL转换
SELECT * FROM users ORDER BY IFNULL(phone, 'ZZZZZ');
四、ORDER BY排序
4.1 单列排序
sql
-- 升序(默认)
SELECT * FROM products ORDER BY price;
SELECT * FROM products ORDER BY price ASC;
-- 降序
SELECT * FROM products ORDER BY price DESC;
-- 按日期排序
SELECT * FROM orders ORDER BY created_at DESC; -- 最新的在前
4.2 多列排序
sql
-- 先按状态排序,再按价格排序
SELECT * FROM products
ORDER BY status DESC, price ASC;
-- 先按分类排序,再按价格降序,最后按ID升序
SELECT * FROM products
ORDER BY category, price DESC, id;
4.3 FIELD自定义排序
有时我们需要按照自定义的顺序排序:
sql
-- 按状态自定义顺序:已支付(1) > 待支付(0) > 已发货(2) > 已完成(3) > 已取消(4)
SELECT * FROM orders
ORDER BY FIELD(status, 1, 0, 2, 3, 4);
-- 按指定ID顺序排序
SELECT * FROM products
WHERE id IN (3, 1, 2)
ORDER BY FIELD(id, 3, 1, 2); -- 结果按3,1,2的顺序
4.4 表达式排序
sql
-- 按价格区间排序(先显示便宜的)
SELECT *,
CASE
WHEN price < 1000 THEN 1
WHEN price < 5000 THEN 2
ELSE 3
END AS price_level
FROM products
ORDER BY price_level, price;
五、LIMIT分页
5.1 基础分页
sql
-- 只返回前10条
SELECT * FROM products LIMIT 10;
-- 返回第11-20条(第2页,每页10条)
SELECT * FROM products LIMIT 10 OFFSET 10;
-- 或简写:
SELECT * FROM products LIMIT 10, 10; -- 从第10条开始,取10条
5.2 带排序的分页
sql
-- 按价格排序后分页
SELECT * FROM products
ORDER BY price ASC
LIMIT 20 OFFSET 40; -- 第3页,每页20条
-- 按创建时间倒序分页(最新数据在前)
SELECT * FROM orders
ORDER BY created_at DESC
LIMIT 10 OFFSET 0; -- 第1页
5.3 分页公式
sql
-- 通用分页公式
-- 第page页,每页pageSize条
-- OFFSET = (page - 1) * pageSize
-- 第5页,每页20条
SET @page = 5;
SET @pageSize = 20;
SET @offset = (@page - 1) * @pageSize;
SELECT * FROM products
ORDER BY id
LIMIT @pageSize OFFSET @offset;
六、实战:电商系统的各种查询场景
6.1 用户相关查询
sql
-- 查询已绑定手机号的活跃用户
SELECT id, username, phone, email
FROM users
WHERE status = 1
AND phone IS NOT NULL
ORDER BY created_at DESC
LIMIT 20;
-- 查询用户名包含zhang的用户
SELECT * FROM users WHERE username LIKE '%zhang%';
-- 查询注册时间在2024年1月的用户
SELECT * FROM users
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01';
6.2 商品相关查询
sql
-- 查询上架且库存充足的商品,按价格排序
SELECT id, name, price, stock
FROM products
WHERE status = 1 AND stock > 0
ORDER BY price ASC
LIMIT 10;
-- 查询价格在1000-5000之间的手机或电脑
SELECT * FROM products
WHERE price BETWEEN 1000 AND 5000
AND category IN ('手机', '电脑')
ORDER BY price DESC;
-- 查询库存不足的商品(需要补货)
SELECT * FROM products WHERE stock < 10 ORDER BY stock ASC;
6.3 订单相关查询
sql
-- 查询最近7天的订单
SELECT * FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY created_at DESC;
-- 查询金额大于10000的已支付订单
SELECT * FROM orders
WHERE total_amount > 10000 AND status = 1
ORDER BY total_amount DESC;
-- 查询特定用户的所有订单
SELECT * FROM orders
WHERE user_id = 1
ORDER BY created_at DESC
LIMIT 10;
-- 查询待支付或已支付的订单(按状态优先级排序)
SELECT * FROM orders
WHERE status IN (0, 1)
ORDER BY FIELD(status, 1, 0), created_at DESC;
6.4 综合查询
sql
-- 查询高价值用户的订单(订单金额大于平均值的订单)
SELECT o.*, u.username
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.total_amount > (SELECT AVG(total_amount) FROM orders)
ORDER BY o.total_amount DESC
LIMIT 10;
-- 查询每个用户的最新订单
SELECT o1.*
FROM orders o1
WHERE o1.created_at = (
SELECT MAX(created_at)
FROM orders o2
WHERE o2.user_id = o1.user_id
)
ORDER BY o1.user_id;
七、踩坑提醒
7.1 LIKE '%xxx%'导致索引失效
sql
-- 有索引的查询(使用索引)
SELECT * FROM users WHERE username = 'zhangsan'; -- 用索引
SELECT * FROM users WHERE username LIKE 'zhang%'; -- 用索引(前缀匹配)
-- 索引失效的查询(全表扫描)
SELECT * FROM users WHERE username LIKE '%zhang'; -- 全表扫描
SELECT * FROM users WHERE username LIKE '%zhang%'; -- 全表扫描
解决方案:
- 避免使用
%xxx%模糊查询 - 使用全文索引(FULLTEXT)
- 使用搜索引擎(Elasticsearch)
- 使用反向索引或分词技术
7.2 类型转换导致索引失效
sql
-- 表结构:phone CHAR(11)
-- 错误:类型转换导致索引失效
SELECT * FROM users WHERE phone = 13800138001; -- 数字,会转换
-- 正确:使用字符串
SELECT * FROM users WHERE phone = '13800138001'; -- 字符串,用索引
7.3 OR条件导致索引失效
sql
-- 可能不走索引(取决于优化器)
SELECT * FROM products WHERE category = '手机' OR price < 1000;
-- 优化方案1:使用UNION
SELECT * FROM products WHERE category = '手机'
UNION ALL
SELECT * FROM products WHERE price < 1000;
-- 优化方案2:使用IN(如果条件适合)
SELECT * FROM products WHERE category IN ('手机', '电脑');
八、面试高频考点
考点1:WHERE和HAVING的区别?
答案:
| 特性 | WHERE | HAVING |
|---|---|---|
| 执行时机 | 分组前过滤 | 分组后过滤 |
| 作用对象 | 原始数据行 | 分组后的结果 |
| 可用条件 | 原始列、表达式 | 聚合函数、别名 |
| 性能 | 更好(先过滤) | 稍差(后过滤) |
sql
-- WHERE:过滤原始行
SELECT category, AVG(price) AS avg_price
FROM products
WHERE status = 1 -- 先过滤上架商品
GROUP BY category;
-- HAVING:过滤分组结果
SELECT category, AVG(price) AS avg_price
FROM products
GROUP BY category
HAVING AVG(price) > 5000; -- 过滤平均价格大于5000的分类
考点2:LIMIT 1000000,10的性能问题?
答案: 深度分页性能很差,因为MySQL需要先扫描1000010行,再返回最后10行。
解决方案:
sql
-- 低效:深度分页
SELECT * FROM orders LIMIT 1000000, 10;
-- 优化方案1:使用覆盖索引
SELECT * FROM orders o
JOIN (SELECT id FROM orders LIMIT 1000000, 10) tmp ON o.id = tmp.id;
-- 优化方案2:使用范围查询(推荐)
SELECT * FROM orders
WHERE id > 1000000
ORDER BY id
LIMIT 10;
-- 优化方案3:记录上次位置
SELECT * FROM orders
WHERE created_at < '上次最后一条的时间'
ORDER BY created_at DESC
LIMIT 10;
考点3:如何优化ORDER BY?
答案:
- 使用索引覆盖排序字段
- 避免使用SELECT *,只查询需要的列
- 减少排序的数据量(先用WHERE过滤)
- 考虑使用内存排序(sort_buffer_size)
sql
-- 有索引的排序(filesort在内存中完成)
SELECT id, name, price FROM products ORDER BY price;
-- 优化:添加复合索引
ALTER TABLE products ADD INDEX idx_status_price (status, price);
-- 查询时使用索引覆盖
SELECT id, name, price FROM products
WHERE status = 1 ORDER BY price; -- 使用索引排序
考点4:IS NULL和= NULL的区别?
答案:
IS NULL是标准SQL语法,用于判断是否为NULL= NULL是错误的写法,任何值与NULL比较都返回NULL(假)- 必须使用
IS NULL或IS NOT NULL判断NULL值
九、总结
今天我们学习了:
- WHERE子句:比较运算符、逻辑运算符、IN、BETWEEN、LIKE
- NULL处理:IS NULL、IFNULL、COALESCE
- ORDER BY排序:单列、多列、FIELD自定义排序
- LIMIT分页:基础分页、深度分页优化
- 实战:电商系统的各种查询场景
核心要点:
- LIKE '%xxx%'会导致索引失效
- 必须用IS NULL判断NULL值
- 深度分页需要优化
- WHERE和HAVING的使用场景不同
十、下一步预告
Day5:MySQL聚合函数与分组
我们将学习:
- COUNT/SUM/AVG/MAX/MIN聚合函数
- GROUP BY分组统计
- HAVING过滤分组
- 复杂统计查询实战
十一、参考资料
互动话题
- 你在工作中遇到过哪些慢查询问题?是怎么优化的?
- 你们项目中的分页是怎么实现的?有没有遇到深度分页的性能问题?
- 对于LIKE模糊查询,你们有什么好的解决方案?
如果觉得有帮助,请点赞+收藏+关注!我们Day5见!