Bootstrap

【趣学SQL】第二章:高级查询技巧 2.2 子查询的高级用法——SQL世界的“俄罗斯套娃“艺术

在这里插入图片描述

第二章:高级查询技巧

2.2 子查询的高级用法——SQL世界的"俄罗斯套娃"艺术

如果说JOIN是数据库的"社交达人",那么子查询就是"逻辑鬼才"——它能像俄罗斯套娃一样,在查询中嵌套查询,甚至让AI都怀疑人生!今天我们将通过一个虚拟的「餐厅订单暴走事件」,揭秘子查询的七重幻境。🍔🔍


2.2.1 标量子查询——单身狗顾客的倔强

-- 找出比平均消费高的顾客(子查询返回单个值)  
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 行子查询——寻找"完美受害者"订单

-- 查找金额最大且最早创建的订单(返回整行数据)  
SELECT *  
FROM orders  
WHERE (amount, created_at) = (  
  SELECT MAX(amount), MIN(created_at)  
  FROM orders  
);  

离奇案例
某顾客在开业首日(2023-01-01)豪掷10万元订了1000份佛跳墙——这条记录将同时满足最大金额和最早订单的条件!

执行逻辑

  1. 子查询先找到金额的最大值和创建时间的最小值
  2. 外层查询匹配这两个条件的"完美受害者"

2.2.3 列子查询——VIP顾客的专属特权

-- 找出所有点过"龙虾伊面"的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——三雄争霸赛

场景:找出有退单记录的顾客
-- 使用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 子查询与外连接——数据缺失的救世主

-- 统计每个地区的销售额(包含零销售地区)  
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:破除嵌套诅咒
-- 优化前(地狱级嵌套)  
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:禁止子查询乱用索引
-- 错误示范(索引失效)  
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"的魔幻之旅》的奇幻世界,记得给你的数据库准备降压药——复杂度即将爆表! 🚀

;