9、编写高级查询:掌握SQL Server 2008的CTE和高级查询技巧

编写高级查询:掌握SQL Server 2008的CTE和高级查询技巧

1. 引言

在SQL Server 2008中,T-SQL语言引入了许多新特性和增强功能,使得编写复杂的查询变得更加容易和高效。特别是公用表表达式(Common Table Expressions, CTE)和窗口函数(Window Functions)等特性,极大地提升了SQL查询的能力。本文将详细介绍如何使用这些高级功能,帮助你在实际工作中更高效地处理和分析数据。

2. 使用公用表表达式(CTE)

公用表表达式(CTE)是一种临时结果集,可以在查询中重复使用。CTE不仅可以简化复杂的查询逻辑,还可以提高查询的可读性和维护性。下面我们通过具体的例子来展示CTE的使用方法。

2.1 单个CTE的使用

CTE的基本语法如下:

WITH cte_name AS (
    SELECT column1, column2, ...
    FROM table_name
)
SELECT *
FROM cte_name;
示例:查询客户订单详情

假设我们有一个名为 Sales.SalesOrderHeader 的表,包含客户的订单信息。我们可以使用CTE来简化查询:

WITH orders AS (
    SELECT SalesOrderID, CustomerID
    FROM Sales.SalesOrderHeader
)
SELECT c.CustomerID, orders.SalesOrderID
FROM Sales.Customer AS c
INNER JOIN orders ON c.CustomerID = orders.CustomerID;

2.2 多个CTE的使用

你还可以在同一查询中定义多个CTE,以进一步简化复杂的查询逻辑。多个CTE的语法如下:

WITH cte_name1 AS (
    SELECT column1, column2, ...
    FROM table_name1
),
cte_name2 AS (
    SELECT column1, column2, ...
    FROM table_name2
)
SELECT *
FROM cte_name1
JOIN cte_name2 ON cte_name1.column = cte_name2.column;
示例:查询客户订单和销售总额
WITH customer_sales AS (
    SELECT CustomerID, SUM(TotalDue) AS TotalSales
    FROM Sales.SalesOrderHeader
    GROUP BY CustomerID
),
average_sales AS (
    SELECT AVG(TotalSales) AS AvgSales
    FROM customer_sales
)
SELECT cs.CustomerID, cs.TotalSales, a.AvgSales
FROM customer_sales AS cs, average_sales AS a
WHERE cs.TotalSales > a.AvgSales;

3. 创建递归CTE

递归CTE允许你在一个CTE中调用自身,从而实现递归查询。这对于处理层次结构数据(如组织结构图)非常有用。递归CTE由两部分组成:锚点成员和递归成员。

3.1 锚点成员

锚点成员是递归查询的起点,通常表示层级结构的根节点。例如,在组织结构图中,锚点成员通常是CEO的记录。

3.2 递归成员

递归成员通过将CTE与原始表连接,逐步构建层级结构。递归成员会不断调用自身,直到没有更多的记录可以处理。

示例:构建组织结构图
WITH OrgChart(EmployeeID, ManagerID, Title, Level, Node) AS (
    -- 锚点成员
    SELECT EmployeeID, ManagerID, Title, 0 AS Level, CONVERT(VARCHAR(30), '/') AS Node
    FROM HumanResources.Employee
    WHERE ManagerID IS NULL
    UNION ALL
    -- 递归成员
    SELECT a.EmployeeID, a.ManagerID, a.Title, b.Level + 1, 
           CONVERT(VARCHAR(30), b.Node + CONVERT(VARCHAR, a.ManagerID) + '/')
    FROM HumanResources.Employee AS a
    INNER JOIN OrgChart AS b ON a.ManagerID = b.EmployeeID
)
SELECT EmployeeID, ManagerID, SPACE(Level * 3) + Title AS Title, Level, Node
FROM OrgChart
ORDER BY Node;

3.3 递归查询的注意事项

  • 默认情况下,递归查询最多执行100次。如果需要调整递归次数,可以使用 OPTION(MAXRECURSION n) 选项。
  • 如果递归查询编写不当,可能会导致无限循环。为了避免这种情况,确保递归成员最终会终止。

4. 使用窗口函数

窗口函数允许你在查询中对数据进行分组和排序,而不影响原始表的结构。常用的窗口函数包括 ROW_NUMBER() 、 RANK() 、 DENSE_RANK() 和 NTILE() 等。这些函数特别适用于需要对数据进行排名或分组的场景。

4.1 使用 NTILE 进行分组

NTILE 函数将结果集划分为指定数量的组(或桶),并为每一行分配一个组号。例如,你可以使用 NTILE 来根据销售人员的业绩分配奖金。

示例:根据销售额分配奖金
SELECT SalesPersonID, SUM(TotalDue) AS TotalSales,
       NTILE(10) OVER (ORDER BY SUM(TotalDue)) * 10000 / COUNT(*) OVER () AS Bonus
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL AND OrderDate BETWEEN '1/1/2004' AND '12/31/2004'
GROUP BY SalesPersonID
ORDER BY TotalSales;
SalesPersonID TotalSales Bonus
274 500000 5000
275 450000 4500
276 400000 4000

4.2 使用 ROW_NUMBER 进行排名

ROW_NUMBER 函数为每一行分配唯一的行号,适用于需要对结果进行排序和排名的场景。

示例:为每个销售人员分配排名
WITH ranked_sales AS (
    SELECT SalesPersonID, SUM(TotalDue) AS TotalSales,
           ROW_NUMBER() OVER (ORDER BY SUM(TotalDue) DESC) AS Rank
    FROM Sales.SalesOrderHeader
    WHERE SalesPersonID IS NOT NULL AND OrderDate BETWEEN '1/1/2004' AND '12/31/2004'
    GROUP BY SalesPersonID
)
SELECT SalesPersonID, TotalSales, Rank
FROM ranked_sales;
SalesPersonID TotalSales Rank
274 500000 1
275 450000 2
276 400000 3

在实际工作中,CTE和窗口函数是非常强大的工具,可以帮助你更高效地处理和分析数据。通过理解和掌握这些高级查询技巧,你将能够编写出更加简洁、高效的SQL查询。接下来的部分将继续探讨更多高级查询技术和应用场景。

5. 高级CTE应用

除了基本的CTE和递归CTE,CTE还有许多高级应用,可以进一步简化复杂的查询逻辑。下面我们将介绍一些常见的高级CTE应用。

5.1 在CTE中调用其他CTE

你可以在一个CTE中调用另一个CTE,从而实现更复杂的查询逻辑。例如,假设我们需要查询员工的工作历史记录,并且希望在查询中包含一些额外的计算字段。

示例:查询员工的工作历史记录
WITH cte1 AS (
    SELECT EmployeeID, EffDate, EffSeq, EmploymentStatus, JobTitle, Salary, ActionDesc
    FROM JobHistory
),
cte2 AS (
    SELECT EmployeeID, MAX(EffDate) AS MaxEffDate
    FROM cte1
    GROUP BY EmployeeID
),
final_query AS (
    SELECT c1.EmployeeID, c1.EffDate, c1.EffSeq, c1.EmploymentStatus, c1.JobTitle, c1.Salary, c1.ActionDesc
    FROM cte1 AS c1
    INNER JOIN cte2 AS c2 ON c1.EmployeeID = c2.EmployeeID AND c1.EffDate = c2.MaxEffDate
)
SELECT *
FROM final_query;

5.2 使用CTE处理复杂业务逻辑

CTE还可以用于处理复杂的业务逻辑,例如计算员工的有效工作记录。通过CTE,你可以逐步构建查询逻辑,使其更易于理解和维护。

示例:计算员工在特定日期的有效工作记录
WITH EffectiveDate AS (
    SELECT EmployeeID, MAX(EffDate) AS MaxEffDate
    FROM JobHistory
    WHERE EffDate <= @Date
    GROUP BY EmployeeID
),
EffectiveSeq AS (
    SELECT EmployeeID, MAX(EffSeq) AS MaxEffSeq
    FROM JobHistory
    WHERE EffDate = (SELECT MaxEffDate FROM EffectiveDate WHERE EmployeeID = JobHistory.EmployeeID)
    GROUP BY EmployeeID
)
SELECT j.EmployeeID, j.EffDate, j.EffSeq, j.EmploymentStatus, j.JobTitle, j.Salary, j.ActionDesc
FROM JobHistory AS j
INNER JOIN EffectiveSeq AS es ON j.EmployeeID = es.EmployeeID AND j.EffSeq = es.MaxEffSeq
ORDER BY j.EmployeeID;

6. 数据操纵语句中的 OUTPUT 子句

OUTPUT 子句是SQL Server 2005引入的一个新特性,允许你在执行数据操纵语句(如 INSERT 、 UPDATE 、 DELETE )时返回或存储受影响的数据。这对于需要跟踪数据变化的场景非常有用。

6.1 使用 OUTPUT 子句返回受影响的数据

示例:返回插入操作后的数据
DECLARE @InsertedData TABLE (EmployeeID INT, FirstName NVARCHAR(50), LastName NVARCHAR(50));

INSERT INTO Employees (FirstName, LastName)
OUTPUT INSERTED.EmployeeID, INSERTED.FirstName, INSERTED.LastName INTO @InsertedData
VALUES ('John', 'Doe');

SELECT * FROM @InsertedData;

6.2 使用 OUTPUT 子句保存受影响的数据

示例:保存更新操作后的数据
DECLARE @UpdatedData TABLE (EmployeeID INT, OldSalary DECIMAL(10, 2), NewSalary DECIMAL(10, 2));

UPDATE Employees
SET Salary = Salary * 1.1
OUTPUT DELETED.EmployeeID, DELETED.Salary AS OldSalary, INSERTED.Salary AS NewSalary INTO @UpdatedData
WHERE DepartmentID = 1;

SELECT * FROM @UpdatedData;

7. 使用 MERGE 语句进行数据同步

MERGE 语句是SQL Server 2008引入的一个强大功能,允许你在一个语句中完成插入、更新和删除操作。这对于需要同步源表和目标表的数据非常有用。

7.1 MERGE 语句的基本语法

MERGE INTO TargetTable AS target
USING SourceTable AS source
ON target.KeyColumn = source.KeyColumn
WHEN MATCHED THEN
    UPDATE SET target.Column1 = source.Column1, target.Column2 = source.Column2
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Column1, Column2)
    VALUES (source.Column1, source.Column2)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;
示例:同步客户数据
MERGE INTO Customers AS target
USING CustomerSource AS source
ON target.CustomerID = source.CustomerID
WHEN MATCHED THEN
    UPDATE SET target.FirstName = source.FirstName, target.LastName = source.LastName
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CustomerID, FirstName, LastName)
    VALUES (source.CustomerID, source.FirstName, source.LastName)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;

8. 总结

通过学习和掌握CTE、窗口函数、 OUTPUT 子句和 MERGE 语句等高级查询技巧,你将能够更高效地处理和分析数据。这些功能不仅简化了复杂的查询逻辑,还提高了查询的可读性和维护性。在实际工作中,合理运用这些技术可以显著提升工作效率。

关键点回顾

  • CTE :简化复杂查询逻辑,提高查询可读性和维护性。
  • 递归CTE :处理层次结构数据,如组织结构图。
  • 窗口函数 :对数据进行分组和排序,适用于排名和分组场景。
  • OUTPUT 子句 :返回或保存数据操纵语句中受影响的数据。
  • MERGE 语句 :在一个语句中完成插入、更新和删除操作,适用于数据同步场景。

下一步学习建议

为了进一步提升你的SQL技能,建议你继续探索SQL Server的其他高级特性,如全文搜索、地理空间数据处理等。同时,多实践和总结经验,不断优化查询性能,以应对日益复杂的业务需求。


通过以上内容的学习,相信你已经掌握了编写高级查询的核心技巧。在实际工作中,灵活运用这些技术将使你能够更高效地处理和分析数据,为你的职业生涯带来更多机会。希望这篇文章能为你提供有价值的参考和指导。

SQL SERVER中CTE语句结构及CTE递归查询 用实例数据解释SQL SERVER中的CTE语句及CTE递归查询 阅读详情

相关推荐

基于Autoware制作高精地图(一)

基于Autoware制作高精地图(一) ​开始进入正题,也是最近在忙的一件事,制作高精地图。 高精地图的制作大概分为以下四个流程(不一定完全正确): 1.构建点云地图; 2.点云地图标注; 3.制作OpenDrive地图; 4.地图存储索引。 这篇文章主要讲解如何构建点云地图。 首先我们要有一段自己录制的激光雷达点云数据,这里推荐大家采用Velodyne16线激光雷达,因为这里我们构建的点云底图是三维的,而采用Velodyne而不是速腾聚创(rslidar)等其他品牌的原因是Autoware里面有一个选项是

weixin_44570248的博客 7797

Windows NPS与华为、华三、思科设备RADIUS认证实战指南

本文详细介绍了Windows NPS与华为、华三、思科设备RADIUS认证的实战配置指南。通过NPS服务器统一管理网络设备认证,实现企业级安全访问控制,涵盖基础配置、设备对接及故障排查方法,助力管理员提升网络安全管理效率。

weixin_29284201的博客 505

【RS】哨兵系列数据下载(哨兵2号Sentinel-2波段介绍、下载)

以哨兵二号为示例,分享如何下载欧空局的哨兵系列数据

Remote Sensing迷途小书童 1万+

城市规划师必备:ArcMap里用这个免费工具,5步完成建筑群通风廊道分析

本文介绍了城市规划中利用ArcMap进行建筑群通风廊道分析的高效方法,重点讲解了迎风面投影指数(FAI)的应用。通过5个步骤,从数据准备到规划应用,帮助规划师快速评估城市通风潜力,优化建筑布局,提升城市风环境质量。

weixin_26800111的博客 412

施耐德 Modicon M580 ePAC 项目实战:从硬件选型到 Control Expert V15 组态

本文详细解析了施耐德 Modicon M580 ePAC 在工业自动化项目中的实战应用,从硬件选型到 Control Expert V15 组态的全流程。重点介绍了水处理站控制系统的硬件配置、网络架构设计、软件安装授权及程序框架开发,帮助工程师高效实施中大型控制系统项目。

weixin_33701617的博客 326

PD详解—第一章—Type-C连接

Type-C连接 今天大师匈下班比较早,再给大家侃一侃PD,希望大家支持。 PD和Type-c的关系 首先讲一下PD快充的实现步骤,本文主要是Source端(电源),Sink端的差不多也一样,只是策略不同而已。 TypeC连接-->广播电源能力Source capbility(数据交互)->打电压电流->等待其他指令。 所以typec连接是第一步,很多人会从字面意思理解typec连接,这不就是把线给插上去就好了吗? 不知道有没有优秀的同学想到正确答案; 正确答案:typec连接的本质是为

大师匈的博客 1万+

uniapp [安卓苹果App端] - 实现获取手机日历读写权限+调用本机日历数据及操作功能+日历中插入事项事件提醒,检测权限手机日历权限是否开启并引导用户同意授权,uniApp app端开启日历授权

uni-app,权限,手机日历权限,安卓Android,苹果ios,uniapp安卓android获取本机日历读写权限使用,uni-app获取日历权限并实现插入事项事件功能 uniapp苹果IOS系统不支持怎么办,判断权限,权限被拒绝了怎么办,获取日历权限报错,读取日历数据农历阴历并操作插入移除,给用户手机日历中放入一条提示,UniApp调用日历进行读写,打包上架后如何获取,安卓ios权限检测,安卓和ios权限获取,uniapp的APP端日历读写权限与使用示例代码,如何开启日历权限,日历的新增事项和删除,判

🏆 前端领域优质创作者 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 64、P4P问题的五个解与控制点的对称分布
下一篇: 5、深入理解卷积神经网络(CNNs)
寂静夜空35
博客等级 码龄3年 59粉丝 1797原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值