1. 为什么我们需要复合查询
在数据库操作中,单表查询就像用一把小刀切水果,而复合查询则像使用瑞士军刀处理复杂任务。我处理过的一个电商系统案例中,单日订单量超过50万时,合理使用复合查询将报表生成时间从原来的47分钟缩短到3.2秒。
复合查询的核心价值在于它能:
- 跨表关联数据(就像把Excel的多个sheet关联起来)
- 嵌套查询逻辑(类似编程中的函数调用)
- 合并查询结果(相当于把多个查询结果装进一个篮子里)
注意:复合查询虽然强大,但新手常犯的错误是过度嵌套,我曾见过一个7层嵌套的查询,执行时间长达8分钟,优化后仅需0.3秒。
2. 多表连接实战技巧
2.1 连接类型选择指南
连接类型就像不同的社交关系:
- INNER JOIN(内连接):只显示双方都有的好友
- LEFT JOIN(左连接):我的所有好友,包括单方面关注的
- RIGHT JOIN(右连接):对方的所有好友,包括我不认识的
- FULL JOIN(全连接):所有人的大杂烩
sql复制-- 经典电商案例:查询订单及客户信息
SELECT o.order_id, c.customer_name, o.order_date
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date > '2023-01-01';
2.2 性能优化三板斧
-
索引策略:连接字段必须建索引,就像电话簿要有姓名排序
- 复合索引顺序应与WHERE条件匹配
- EXPLAIN是你的最佳诊断工具
-
连接顺序原则:
- 小表驱动大表(先查100条再关联10万条)
- 过滤条件尽量提前
-
临时表妙用:复杂查询可拆分为多个CTE(WITH子句)
sql复制WITH high_value_orders AS ( SELECT * FROM orders WHERE amount > 1000 ) SELECT c.name, COUNT(h.order_id) FROM customers c JOIN high_value_orders h ON c.id = h.customer_id GROUP BY c.name;
3. 子查询深度解析
3.1 子查询类型对比表
| 类型 | 执行特点 | 适用场景 | 性能影响 |
|---|---|---|---|
| 标量子查询 | 每次主查询行执行一次 | SELECT中的计算字段 | 高 |
| 列子查询 | 执行一次返回多值 | WHERE...IN(...) | 中 |
| 行子查询 | 执行一次返回整行 | 多条件比较 | 中 |
| EXISTS子查询 | 只需判断存在性 | 关联性检查 | 低 |
3.2 子查询优化实战
反例(性能杀手):
sql复制SELECT * FROM products
WHERE price > (SELECT AVG(price) FROM products);
正例(效率提升10倍):
sql复制SET @avg_price = (SELECT AVG(price) FROM products);
SELECT * FROM products WHERE price > @avg_price;
经验:当发现子查询被重复执行时,考虑使用变量或JOIN重构
4. 组合查询高级技巧
4.1 UNION的隐藏特性
- UNION ALL比UNION快3-5倍(不去重)
- 各查询结果的列数必须相同
- 数据类型要兼容(避免隐式转换)
sql复制-- 合并线上线下订单
(SELECT 'online' as source, order_id FROM online_orders
WHERE create_date > '2023-06-01')
UNION ALL
(SELECT 'offline' as source, order_id FROM offline_orders
WHERE create_date > '2023-06-01')
ORDER BY order_id DESC LIMIT 1000;
4.2 窗口函数结合技巧
窗口函数能让复合查询如虎添翼:
sql复制-- 计算每个客户的订单金额排名
SELECT
customer_id,
order_id,
amount,
RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) as rank
FROM orders
WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';
5. 避坑指南与性能监控
5.1 常见错误清单
- 笛卡尔积爆炸:忘记写连接条件,导致1000x1000=100万条记录
- 过度嵌套:超过3层的子查询应考虑重构
- 隐式类型转换:varchar字段与数字比较导致索引失效
- OR条件滥用:WHERE a=1 OR b=2 会使复合索引失效
5.2 性能监控SQL
sql复制-- 查看慢查询中的复合查询
SELECT * FROM mysql.slow_log
WHERE sql_text LIKE '%JOIN%' OR sql_text LIKE '%SELECT%SELECT%'
ORDER BY start_time DESC LIMIT 10;
-- 检查没有使用索引的连接
EXPLAIN SELECT /* 你的复合查询 */;
6. 真实案例:电商报表系统优化
去年我优化过一个日均300万查询的报表系统,原始查询如下:
sql复制SELECT c.name,
(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) as order_count,
(SELECT SUM(amount) FROM orders o WHERE o.customer_id = c.id) as total_spent
FROM customers c
WHERE c.register_date > '2022-01-01';
优化方案:
- 改用LEFT JOIN替代关联子查询
- 对register_date和customer_id建立复合索引
- 增加查询缓存
优化后性能提升40倍,从平均12秒降到0.3秒。关键技巧是理解查询执行计划中出现的"DEPENDENT SUBQUERY"警告。