当前位置: 首页 > news >正文

SQL 子查询:解锁复杂查询的秘密

SQL 子查询:解锁复杂查询的秘密

您知道基本的 SQL。您可以使用 SELECT 获取数据,使用 WHERE 过滤,并连接表。但现在您的经理问道:“找出所有价格高于平均值的产品”或“显示有超过 5 个订单的客户。”

您盯着屏幕,尝试编写查询……然后卡住了。

原因在于:基本的 SQL 教您处理现有数据。但现实世界的查询通常需要多步逻辑——先计算某些内容,然后使用该计算结果来过滤或转换数据。

这就是 SQL 子查询的用武之地。它们是嵌套在其他查询中的查询,让您将复杂问题分解成可管理的步骤。

子查询:查询中的查询

子查询只是嵌套在另一个查询中的 SELECT 语句。内层查询先运行,外层查询使用其结果。

想象它像编程中的函数调用——您调用函数获取值,然后在主代码中使用该值。

这是最简单的示例:

-- 找出薪资高于平均值的员工
SELECT first_name, last_name, salary
FROM employees
WHERE salary > (SELECT AVG(salary)FROM employees
);

工作原理:

  1. 内层查询先运行:SELECT AVG(salary) FROM employees → 返回 60000

  2. 外层查询使用该结果:WHERE salary > 60000

  3. 您得到薪资高于平均值的员工列表

美妙之处?您无需单独计算平均值并硬编码。它是动态的——如果薪资变化,平均值会自动更新。

三种类型的子查询

子查询根据返回的内容有三种“形状”:

  • 标量子查询:返回单个值(1 行,1 列)——如 AVG(salary) 或 MAX(price)

  • 列子查询:返回一列多行——如客户 ID 列表

  • 表子查询:返回多行多列——如聚合结果的小型表

视觉类比:将子查询想象成在边上做数学作业,然后在主线上写最终答案。边上的工作帮助您解决主要问题。

现在让我们看看三个实际可以使用这些子查询的位置。

位置 #1:WHERE 子句——智能过滤

WHERE 子句是最常见的子查询位置。您使用它们基于计算值或其他表的列表来过滤行。

示例 1:与平均值比较

想要找出价格高于平均值的产品?简单:

-- 价格高于平均值的产品
SELECT product_name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

为什么这比手动操作更好:

  • 无需运行两个查询

  • 无需硬编码过时的值

  • 一行代码,自解释

示例 2:使用 IN 检查成员资格

需要找出下过订单的客户?使用 IN 和子查询:

-- 下过订单的客户
SELECT customer_id, first_name, last_name
FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders
);

发生了什么:

  1. 内层查询返回列表:[1, 5, 8, 12, 15, ...]

  2. 外层查询检查每个 customer_id 是否出现在该列表中

  3. 只返回匹配的客户

示例 3:与特定值比较

您也可以与单个记录比较:

-- 薪资高于 Sarah Connor 的员工
SELECT first_name, last_name, salary
FROM employees
WHERE salary > (SELECT salary FROM employees WHERE first_name = 'Sarah' AND last_name = 'Connor'
);

可以使用的运算符

对于标量子查询(返回一个值):

  • , <, =, >=, <=, <>(所有比较运算符)

对于列子查询(返回列表):

  • IN, NOT IN(成员测试)

💡 经验法则:如果您需要与 AVG、MAX、MIN 比较,或检查值是否存在于另一个表中,请在 WHERE 中使用子查询。

位置 #2:SELECT 子句——添加计算列

如果您想为每行添加计算列呢?这就是 SELECT 子句子查询的用武之地。

这是一个常见场景——显示每个客户及其订单数:

SELECT customer_id,first_name,last_name,(SELECT COUNT(*)FROM orders oWHERE o.customer_id = c.customer_id) AS order_count
FROM customers c;

这里的区别:

这是一个相关子查询——它引用外层查询的表(c.customer_id)。对于每个客户行,子查询运行并计算该客户的订单数。

很酷,对吧?您添加了一个动态列,而无需更改表结构。

何时使用(以及何时不使用)

⚠️ 性能警告:SELECT 子查询每外层行运行一次。如果有 10,000 个客户,该子查询执行 10,000 次。对于大表的生产查询,请考虑这个 JOIN 替代方案:

-- 对于大数据集更快
SELECT c.customer_id, c.first_name, c.last_name, COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name;

使用 SELECT 子查询进行快速分析。使用 JOIN 用于性能关键的生产代码。

位置 #3:FROM 子句——强大一招

这是子查询真正强大的地方。您可以在 FROM 子句中使用子查询来创建派生表——一个临时结果集,您可以像查询任何常规表一样查询它。

问题

假设您想找出下过超过 5 个订单的客户。您尝试这个:

-- ❌ 错误 - 这不会工作!
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE COUNT(*) > 5  -- 错误!
GROUP BY customer_id;

为什么失败:WHERE 在分组前过滤原始行。此时,COUNT() 还不存在。您无法在 WHERE 中使用聚合。

解决方案:派生表

在 FROM 中使用子查询先聚合,然后过滤:

-- ✅ 正确 - 查询聚合结果
SELECT *
FROM (SELECT customer_id,COUNT(*) AS order_count,SUM(total_amount) AS total_spentFROM ordersGROUP BY customer_id
) AS customer_summary
WHERE order_count > 5;

发生了什么:

  1. 内层查询按客户聚合订单 → 创建一个临时“表”

  2. 外层查询将其视为常规表,并在 WHERE order_count > 5 中过滤

  3. 别名(AS customer_summary)是 SQL 语法要求的

想象成动态创建一个虚拟表,然后查询该表。它就像编写一个小型报告,然后分析该报告。

真实世界示例

让我们更复杂一些。找出每个产品类别中消费超过 $500 的客户:

SELECT category,customer_id,first_name,total_spent
FROM (SELECT p.category,c.customer_id,c.first_name,SUM(oi.quantity * oi.unit_price) AS total_spentFROM customers cJOIN orders o ON c.customer_id = o.customer_idJOIN order_items oi ON o.order_id = oi.order_idJOIN products p ON oi.product_id = p.product_idGROUP BY p.category, c.customer_id, c.first_name
) AS customer_spending
WHERE total_spent > 500
ORDER BY category, total_spent DESC;

分解:

  • 内层查询:计算每个客户在每个类别中的总消费(需要多个 JOIN 和 GROUP BY)

  • 外层查询:过滤消费 >$500 的客户,并按类别排序

没有派生表,您需要临时表或多个查询。有了它们,就是一个干净的语句。

替代方案:HAVING 子句

对于简单情况,HAVING 也有效:

-- 对于简单聚合过滤也正确
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5;

当从同一表过滤聚合时,使用 HAVING。当需要中间结果或复杂多表计算时,使用 FROM 子查询。

💡 这样想:派生表让您将复杂查询分解成步骤。第 1 步:创建所需数据。第 2 步:查询该数据。这是 SQL 的“分而治之”。

您的子查询速查表

让我们总结每个位置何时使用:

WHERE 子句

最佳用于:基于计算或成员资格过滤行

-- 与平均值/最大值/最小值比较
WHERE price > (SELECT AVG(price) FROM products)-- 检查值是否存在于另一个表中
WHERE customer_id IN (SELECT customer_id FROM orders)

SELECT 子句

最佳用于:添加计算列(仅限小数据集!)

-- 为每行添加计数/求和/计算
SELECT name, (SELECT COUNT(*) FROM orders WHERE ...) AS order_count

FROM 子句

最佳用于:查询聚合数据或中间结果

-- 创建临时聚合表,然后查询它
SELECT *
FROM (SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id) AS counts
WHERE counts.order_count > 5

快速决策树

需要过滤行?
├─ 与 AVG/MAX/MIN 比较? → WHERE 子查询
└─ 过滤聚合? → FROM 子查询 + WHERE需要添加列?
├─ 小数据集? → SELECT 子查询
└─ 大数据集? → 使用 JOIN 代替需要中间结果?
└─ FROM 子查询(派生表)

您已准备好编写更智能的查询

您学到了什么:

  • WHERE 子查询:基于计算值或其他表的列表过滤行

  • SELECT 子查询:添加动态列(在大表上小心使用!)

  • FROM 子查询:创建中间结果进行查询——“分而治之”方法

进展:WHERE 过滤 → SELECT 添加 → FROM 转换。

每个位置都建立在上一个的基础上。先掌握 WHERE 比较。然后尝试 SELECT 计算。最后,解锁 FROM 中的派生表的全部威力。

练习挑战

提示:您需要 FROM 子查询先聚合消费,然后过滤和排序。在检查答案前试试!

解决方案:

SELECT customer_id, first_name, last_name, total_spent
FROM (SELECT c.customer_id,c.first_name,c.last_name,SUM(o.total_amount) AS total_spentFROM customers cJOIN orders o ON c.customer_id = o.customer_idGROUP BY c.customer_id, c.first_name, c.last_name
) AS customer_totals
ORDER BY total_spent DESC
LIMIT 5;
http://www.dtcms.com/a/589746.html

相关文章:

  • 临沂网络网站建设长尾关键词举例
  • vs code jupyter连gpu结点kernel
  • 设计网站用户需求分析报告甘肃seo网站
  • QUSB BULK和Qualcomm HS-USB QDLoader 9008的区别是什么?
  • 读后感:《解析极限编程:拥抱变化》
  • 【愚公系列】《MCP协议与AI Agent开发》008-MCP的基本原理(MCP的状态管理与中间态控制)
  • 天津网站建设方案咨询深圳世展建设公司
  • 企业碳资产的清算(核算)、审计和交易全过程
  • 珠海学网站开发网页可视化编辑
  • 简单网站建设模板微信扫码即可打开的网站如何做
  • 企业网站设计模板网站建设策划实施要素有哪些
  • AIGC|广州AI优化企业新榜单与选择指南
  • 不同种类(如红参、白参)或不同产地人参的化学成分有何差异?
  • 南昌网站建设公司案例wordpress火车头免密发布模块接口
  • 网站网页框架构架图怎么做南通网站推广排名
  • Git Tag 用法记录
  • 《论文写作》笔记
  • 三明网站制作上传网站怎么安装
  • fastmcp 纳米AI 报错 uniq_id Unexpected keyword argument 多余参数报错
  • 黄石市下陆区建设管理局网站专业网站建设制作公司哪家好
  • 视觉信息如何被大脑处理?
  • 建设银行流水网站wordpress documentation
  • 【本地持久化到磁盘的模式】
  • 土巴兔网站开发技术软件开发的七个流程
  • 给公司做网站软件网站备案点不进去
  • 11、Linux 文本流处理命令
  • 微信网站建设方案ppt湖北省住房和城乡建设网站
  • Linux复习:进程状态与环境变量深度解析:孤儿、僵尸进程与程序启动探究
  • JVM(二)------ 类加载、初始化与单例模式的联系
  • 做【秒开】的程序:WPF / WinForm / WinUI3 / Electron