讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

updream

一键生成高质量推广成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

Laper
Laper

剧本策划到成片协作

SQL Server如何利用公用表表达式进行分组汇总_通过CTE结构实现

阿敏同学_7314

阿敏同学_7314

发布时间:2026-06-16 12:56:35

|

434人浏览过

|

来源于php中文网

原创

CTE在分组场景的核心价值是提前提炼分组逻辑供主查询复用,而非直接执行汇总;必须在CTE内完成全部聚合,主查询仅轻量关联,否则性能受损且成果被绕过。

sql server如何利用公用表表达式进行分组汇总_通过cte结构实现

CTE 本身不执行分组汇总,它只是把分组逻辑“提前提炼出来”,让主查询能干净地复用结果——这才是它在分组场景下真正不可替代的价值。

为什么不能直接在 CTE 里写 GROUP BY 后再 JOIN?

很多人试图这样写:

WITH sales_agg AS (
  SELECT SalesPersonID, COUNT(*) AS cnt
  FROM Sales.SalesOrderHeader
  GROUP BY SalesPersonID
),
order_detail_agg AS (
  SELECT SalesOrderID, SUM(OrderQty) AS total_qty
  FROM Sales.SalesOrderDetail
  GROUP BY SalesOrderID
)
SELECT s.SalesPersonID, s.cnt, d.total_qty
FROM sales_agg s
JOIN Sales.SalesOrderHeader h ON s.SalesPersonID = h.SalesPersonID
JOIN order_detail_agg d ON h.SalesOrderID = d.SalesOrderID;

问题在于:主查询又把 SalesOrderHeader 拉进来做 JOIN,等于绕过了 sales_agg 的聚合成果,中间结果集可能暴涨数倍。CTE 的价值被完全浪费。

正确做法是:所有聚合必须在 CTE 内完成,主查询只做轻量关联或过滤。

  • CTE 中的 GROUP BY 必须覆盖最终需要的所有分组维度(比如既要按人、又要按年,就得在 CTE 里一起分)
  • 如果主表小、明细表大(如 orders 表 10 万行,order_items 表 800 万行),先在 CTE 里对 order_items 按 order_id 聚合,再 JOIN orders,性能提升往往超过 5 倍
  • SQL Server 不会自动物化 CTE;若同一个 CTE 被引用多次(如既用于 JOIN 又用于 WHERE EXISTS),优化器可能重复执行其内部查询——这时应考虑用临时表替代

多个分组逻辑共存时怎么组织 CTE?

当你要同时计算「每个销售员的订单数」「每个销售员的退货数」「每个销售员的平均订单金额」,别堆在一个 CTE 里硬塞所有字段,容易失控。

推荐分层定义:

WITH sales_orders AS (
  SELECT SalesPersonID, COUNT(*) AS order_cnt, AVG(TotalDue) AS avg_order_amt
  FROM Sales.SalesOrderHeader
  WHERE Status = 5  -- 已发货
  GROUP BY SalesPersonID
),
sales_returns AS (
  SELECT SalesPersonID, COUNT(*) AS return_cnt
  FROM Sales.SalesOrderHeader h
  JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID
  WHERE d.ProductID IN (SELECT ProductID FROM Production.Product WHERE FinishedGoodsFlag = 0)
  GROUP BY SalesPersonID
)
SELECT 
  o.SalesPersonID,
  o.order_cnt,
  ISNULL(r.return_cnt, 0) AS return_cnt,
  o.avg_order_amt
FROM sales_orders o
LEFT JOIN sales_returns r ON o.SalesPersonID = r.SalesPersonID;
  • 每个 CTE 职责单一,调试和修改都只影响局部
  • sales_returns 里嵌套了子查询,但这是允许的;CTE 内部可以含任意合法 SELECT,只要不出现在递归成员中
  • 列名列表 (SalesPersonID, order_cnt, ...) 在多 CTE 场景下建议显式写出,避免因字段顺序错位导致静默错误

递归 CTE 里能做分组汇总吗?

不能。SQL Server 明确禁止在递归成员中使用 GROUP BY、SUM()、COUNT()、DISTINCT 等聚合操作。

典型错误写法:

WITH dept_tree AS (
  SELECT DepartmentID, ParentID, 1 AS Level
  FROM HumanResources.Department
  WHERE ParentID IS NULL
  UNION ALL
  SELECT d.DepartmentID, d.ParentID, t.Level + 1
  FROM HumanResources.Department d
  INNER JOIN dept_tree t ON d.ParentID = t.DepartmentID
  GROUP BY d.DepartmentID, d.ParentID  -- ❌ 报错:递归成员中不允许 GROUP BY
)

正确路径永远是两步分离:

  • 第一步:用递归 CTE 展开完整层级关系(例如生成 RootID → DescendantID 映射表)
  • 第二步:用这个结果 JOIN 员工表,再在外层 GROUP BY RootID 汇总人数或薪资
  • 锚点成员和递归成员之间必须用 UNION ALL,且递归成员的列数、类型、顺序必须与锚点严格一致

CTE 分组后怎么更新原始表?

CTE 可以作为 UPDATE、DELETE 的目标,但前提是 CTE 引用的必须是单个基表,且不能含聚合、DISTINCT、窗口函数等不可更新元素。

比如你想把每个部门的员工数写回 Department 表的 EmployeeCount 字段:

WITH dept_emp_count AS (
  SELECT d.DepartmentID, COUNT(e.BusinessEntityID) AS emp_cnt
  FROM HumanResources.Department d
  LEFT JOIN HumanResources.EmployeeDepartmentHistory e ON d.DepartmentID = e.DepartmentID
  GROUP BY d.DepartmentID
)
UPDATE d
SET d.EmployeeCount = c.emp_cnt
FROM HumanResources.Department d
INNER JOIN dept_emp_count c ON d.DepartmentID = c.DepartmentID;
  • 这里 dept_emp_count 是纯聚合 CTE,不能直接 UPDATE dept_emp_count —— 它不是可更新的视图
  • 必须通过 JOIN 回原始表 Department,再在 UPDATE 子句中指定目标
  • 若 CTE 包含 JOIN 多个表或聚合,就只能用于 SELECT,别指望它能当更新入口

最易被忽略的一点:CTE 的生命周期只限于紧随其后的那一条语句。写完 WITH ... AS (...) SELECT ... 之后,那个名字就彻底失效了——想复用,要么重写,要么转成临时表或视图。

相关文章

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

4883

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

991

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

1289

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

6921

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

3603

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

6920

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

9641

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

1310

2024.04.29

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

60

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn