第二章:高级查询技巧
2.2 子查询的高级用法------SQL世界的"俄罗斯套娃"艺术
如果说JOIN是数据库的"社交达人",那么子查询就是"逻辑鬼才"------它能像俄罗斯套娃一样,在查询中嵌套查询,甚至让AI都怀疑人生!今天我们将通过一个虚拟的「餐厅订单暴走事件」,揭秘子查询的七重幻境。🍔🔍
2.2.1 标量子查询------单身狗顾客的倔强
sql
-- 找出比平均消费高的顾客(子查询返回单个值)
SELECT customer_name, total_spent
FROM customers
WHERE total_spent > (
SELECT AVG(total_spent)
FROM customers
);
输出暴击:
+---------------+--------------+
| customer_name | total_spent |
+---------------+--------------+
| 土豪张 | 8888.00 |
| 暴发户李 | 6666.00 |
+---------------+--------------+
经典场景:
- 当老板说"给我找出比平均水平高的数据"时
- 相当于让数学课代表先算全班平均分,再挑出优等生
冷知识:标量子查询如果返回多行结果,数据库会直接报错------就像微波炉同时加热两碗泡面,必定翻车!
2.2.2 行子查询------寻找"完美受害者"订单
sql
-- 查找金额最大且最早创建的订单(返回整行数据)
SELECT *
FROM orders
WHERE (amount, created_at) = (
SELECT MAX(amount), MIN(created_at)
FROM orders
);
离奇案例 :
某顾客在开业首日(2023-01-01)豪掷10万元订了1000份佛跳墙------这条记录将同时满足最大金额和最早订单的条件!
执行逻辑:
- 子查询先找到金额的最大值和创建时间的最小值
- 外层查询匹配这两个条件的"完美受害者"
2.2.3 列子查询------VIP顾客的专属特权
sql
-- 找出所有点过"龙虾伊面"的VIP顾客
SELECT *
FROM vip_customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
WHERE dish_name = '龙虾伊面'
);
幽默映射:
- 主查询:银行VIP休息室
- 子查询:拿着"龙虾伊面"消费记录当入场券
优化技巧 :
当子查询结果很大时,改用EXISTS
更高效(后文揭晓)
2.2.4 子查询 vs IN vs EXISTS------三雄争霸赛
场景:找出有退单记录的顾客
sql
-- 使用IN
SELECT *
FROM customers
WHERE id IN (
SELECT customer_id
FROM refund_records
);
-- 使用EXISTS(更高效!)
SELECT *
FROM customers c
WHERE EXISTS (
SELECT 1
FROM refund_records r
WHERE r.customer_id = c.id
);
执行原理:
IN
先执行子查询生成列表,再逐个比对------像查纸质名单EXISTS
像智能安检门,发现匹配立即放行
性能测试 :
当子查询结果超过1万条时,EXISTS
的速度优势就像高铁 vs 自行车!
2.2.5 子查询与外连接------数据缺失的救世主
sql
-- 统计每个地区的销售额(包含零销售地区)
SELECT
r.region_name,
COALESCE(
(SELECT SUM(amount)
FROM orders o
WHERE o.region_id = r.region_id),
0
) AS total_sales
FROM regions r;
输出故事:
+-------------+-------------+
| 华东区 | 158800.00 |
| 西北区 | 0.00 | ← 这里的羊群只吃草不下单!
+-------------+-------------+
技术要点:
COALESCE
处理NULL值- 子查询替代LEFT JOIN + GROUP BY的复杂写法
2.2.6 子查询性能优化------拯救慢查询的七种武器
武器1:破除嵌套诅咒
sql
-- 优化前(地狱级嵌套)
SELECT *
FROM A
WHERE id IN (
SELECT a_id
FROM B
WHERE created_at > (
SELECT MAX(created_at) - INTERVAL 7 DAY
FROM C
)
);
-- 优化后(使用CTE分拆)
WITH latest_c AS (
SELECT MAX(created_at) - INTERVAL 7 DAY AS cutoff
FROM C
)
SELECT A.*
FROM A
JOIN B ON A.id = B.a_id
CROSS JOIN latest_c
WHERE B.created_at > latest_c.cutoff;
武器2:禁止子查询乱用索引
sql
-- 错误示范(索引失效)
SELECT *
FROM orders
WHERE YEAR(created_at) = (
SELECT MAX(YEAR(created_at))
FROM orders
);
-- 正确姿势
SELECT *
FROM orders
WHERE created_at >= (
SELECT DATE_FORMAT(MAX(created_at), '%Y-01-01')
FROM orders
);
血泪案例 :
某DBA优化时发现,一个子查询导致800万行表扫描------罪魁祸首是子查询中的UPPER(email)
让索引失效,就像用磁铁找塑料钥匙!
课后彩蛋:子查询的黑暗历史
- 在SQL-92标准前,子查询被称为"嵌套查询"并被视为黑魔法
- 早期Oracle数据库处理多级子查询时,曾出现"查询套娃超过32层自动崩溃"的BUG
- 某程序员用子查询自动生成诗歌,结果被老板发现后要求改成写报表
现在你已经成为"子查询领域的套娃大师"!下一章我们将进入《数据清洗技术------给数据库做"数据SPA"的魔幻之旅》的奇幻世界,记得给你的数据库准备降压药------复杂度即将爆表! 🚀