SQL Server-聚焦APPLY运算符(二十七)

前言

其实有些新的特性在SQL Server早就已经出现过,但是若非系统的去学习数据库你会发现在实际项目中别人的SQL其实是比较复杂的,其实利用新的SQL Server语法会更加方便和简洁,从本节开始我们将讲述一些SQL Server中早已出现的新语法,简短的内容,深入的理解,Always to reivew the basics。

初探APPLY运算符

APPLY运算符是一个非常强大的表运算符,但是APPLY不是标准的,相对应的标准叫做LATERAL,但是此标准并未在SQL Server中实现。像所有表运算符一样,该运算符用于查询的FROM子句中。APPLY运算符支持的类型是CROSS APPLY和OUTER APPLY。CROSS APPY仅仅实施一个逻辑查询处理阶段,而OUTER APPLY实施了两个阶段,APPLY运算符对两个输入表进行操作,第二个可以是一个表表达式,我们将APPLY两侧的表分别叫做左侧表和右侧表,右侧表通常是一个派生表或TVF(内嵌表值函数)。CROSS APPLY运算符实施一个逻辑查询处理阶段-它将右侧的表表达式应用到左侧表的每一行,并生成一个组合结果集的结果表。CROSS APPLYl类似于交叉联接中的CROSS JOIN,但是使用CROSS APPLY运算符,右侧的表表达式可以对来自左侧表的每一行表示一个不同的行集,这是与联接的不同之处。当在右侧使用一个派生表,并且派生表查询中引用来自左侧表的属性,就可以实现此目标,或者是在右侧使用一个内嵌TVF,可以传递左侧的属性作为输入参数,同样可以实现此目的-摘抄自SQL Server 2012基础教程。下面我们看一个简单的例子。

USE TSQL2012
GOSELECT C.custid, A.orderid, A.orderdate
FROM Sales.Customers AS CCROSS APPLY(SELECT TOP(3) orderid, empid, orderdate, requireddate FROM Sales.Orders AS OWHERE O.custid = C.custidORDER BY orderdate DESC, orderid DESC) AS A;

上述完成的是返回每个客户最近的3个订单。我们可以将右侧的表表达式看做是一个相关子查询,右侧的表表达式通过引用custid对来自Customers表的每一行进行处理并返回每个客户的最近的3个订单,是不是看起来很清爽呢,下面我们将进一步探讨APPLY运算符的作用。

进一步探讨APPLY运算符

上面我们看到通过相关子查询来进行查询显得代码有点丑陋,我们再来看一个例子。查询每个单价最高的订单,我们通过子查询来实现。

CROSS APPLY

USE AdventureWorks2012
GOSELECT SalesOrderID,OrderDate,MaxUnitPrice =(SELECT MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID)
FROM Sales.SalesOrderHeader AS soh

如上操作看似代码比较简洁也能完成我们的查询诉求,但是我们用派生表来进行查询又是怎样的呢? 

USE AdventureWorks2012
GOSELECT soh.SalesOrderID,soh.OrderDate,sod.max_unit_price
FROM Sales.SalesOrderHeader AS soh
JOIN
(SELECT max_unit_price = MAX(sod.UnitPrice),SalesOrderIDFROM Sales.SalesOrderDetail AS sodGROUP BY sod.SalesOrderID
) sod
ON sod.SalesOrderID = soh.SalesOrderID

此时由于两个表完全不相关,我们需要通过GROUP BY完成再进行JOIN,代码不是显得非常臃肿吗,这还是简单的,当有多个表时就比较复杂了,导致代码就不再具有可读性。但是自从在SQL Server 2005中有了APPLY妈妈再也不用担心我读不懂复杂的代码了,我们看看CROSS APPLY是怎样实现的。

USE AdventureWorks2012
GOSELECT soh.SalesOrderID,soh.OrderDate,sod.max_unit_price
FROM Sales.SalesOrderHeader AS soh
CROSS APPLY
(SELECT max_unit_price = MAX(sod.UnitPrice)FROM Sales.SalesOrderDetail AS sodWHERE soh.SalesOrderID = sod.SalesOrderID
) sod

当我们利用内部联接时此时JOIN中的查询是独立的所以需要进行GROUP BY,而对于CROSS APPLY它本身就是对来自左侧的表中每一行就行处理并返回,同时利用CROSS APPLY它也超越了相关子查询,比如说我们还需要查出每个订单的总价呢,我们利用相关子查询需要再次嵌入SELECT子句。

SELECT SalesOrderID           ,OrderDate              ,MaxUnitPrice           = (SELECT MAX(sod.UnitPrice) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID),SumLineTotal           = (SELECT SUM(LineTotal) FROM Sales.SalesOrderDetail sod WHERE soh.SalesOrderID = sod.SalesOrderID)
FROM Sales.SalesOrderHeader AS soh

而利用CROSS APPLY只需添加集合函数SUM即可

USE AdventureWorks2012
GOSELECT soh.SalesOrderID,soh.OrderDate,sod.max_unit_price,sod.sum_line_total
FROM Sales.SalesOrderHeader AS soh
CROSS APPLY
(SELECT max_unit_price = MAX(sod.UnitPrice),sum_line_total = SUM(sod.LineTotal)FROM Sales.SalesOrderDetail AS sodWHERE soh.SalesOrderID = sod.SalesOrderID
) sod 

OUTER APPLY

对于OUTER APPLY,如果右侧的表表达式返回一个空集合,CROSS APPLY运算符不会返回相应的左侧行,也就是说OUTER APPLY和在派生表上进行LEFT JOIN是等同的,如下:

SELECT soh.SalesOrderID,soh.OrderDate,sod.max_unit_price
FROM Sales.SalesOrderHeader AS soh
LEFT JOIN
(SELECT max_unit_price = MAX(sod.UnitPrice),SalesOrderIDFROM Sales.SalesOrderDetail AS sodGROUP BY sod.SalesOrderID
) sod
ON sod.SalesOrderID = soh.SalesOrderID

此时我们利用OUTER APPLY则是如下:

USE AdventureWorks2012
GOSELECT soh.SalesOrderID,soh.OrderDate,sod.max_unit_price
FROM Sales.SalesOrderHeader AS soh
OUTER APPLY
(SELECT max_unit_price = MAX(sod.UnitPrice)FROM Sales.SalesOrderDetail AS sodWHERE soh.SalesOrderID = sod.SalesOrderID
) sod

上述对于APPLY右侧表表达式是一个派生表,此时为了封装,我们可以使用TVF内嵌表值函数来实现。其实将内嵌表值函数来代替派生表实现每个客户最近的3个订单。首先我们封装一个表值函数

USE TSQL2012
GOIF OBJECT_ID('dbo.TopOrders') IS NOT NULLDROP FUNCTION dbo.TopOrders;
GOCREATE FUNCTION dbo.TopOrders(@custid  AS INT, @n  AS  INT)RETURNS TABLE
AS RETURNSELECT  orderid, empid, orderdate, requireddateFROM Sales.OrdersWHERE  custid = @custidORDER BY orderdate DESC, orderid DESCOFFSET 0 ROWS FETCH FIRST @n ROWS ONLY;
GO

接着利用CROSS APPLY进行查询。

USE TSQL2012
GOSELECT C.custid, C.companyname, A.orderid, A.empid, A.requireddate
FROM Sales.Customers AS CCROSS APPLY dbo.TopOrders(C.custid, 3) AS A;

上面我们通过封装内嵌表值函数代替派生表使代码更具可读性和可维护性。到此我们可以得出一点基本结论。

APPLY运算符使用分析结论:当需要对表中的每一行进行应用时,且需要将所有结果集组合到一个结果集表中时,此时我们应该使用APPLY运算符,至于是使用CROSS APPLY还是OUTER APPLY根据场景而定,虽然APPLY右侧表可以用相关子查询或者派生表来实现,但是使得代码臃肿和可维护性差,通过封装内嵌表值函数来实现可以说是对右侧表通过相关子查询或者派生表来实现的完美替代者。

总结

本节我们讲解了APPLY运算符中两种类型的使用,下一节我们来分析下关于CROSS APPLY VS INNER JOIN的性能问题,同时也说明下CROSS APPLY和OUTER APPLY的应用场景。简短的内容,深入的理解,我们下节再会。

转载于:https://www.cnblogs.com/CreateMyself/p/6193183.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php中的解析范围符,如何合理使用php7范围解析操作符

class Base{const mynum1111;//基类常量public static $num11;//基类静态成员变量public $namebase zhangsan;//基类普通成员变量public function hi(){//普通成员函数echo base普通方法-调用自己的常量.self::mynum.;//1111echo base普通方法-调用自己的静态成员变量.self::$n…

php项目课题,php课题

异步调用 功能说明 桶相关接口和对象相关接口均支持以“Async”结尾的方式进行异步调用(例如,同步方法为ObsClient->putObject,则异步方法为ObsClient->putObjectAsync)。异步调用完成后会将返回结果输出到回调函数中,回调函数依次包含…

vs2013和vs2010的配置

win10下vs2013和vs2010的相关配置 ------made by siwuxie095 主要是推荐一些vs2013和vs2010的好用的插件和配色方案,下面主要是以vs2013示例 1、首先是配色方案,一般情况下安装好了IDE之后,为了护眼,都是直接调成了暗色调或深色…

php 伪静态是什么意思,路由与伪静态

## 路由与伪静态### 路由>[info] 字面上解释,路是线路,道路;由,缘由,指意,表示规则;所以意思就是按照什么规则,或者意义,被指派到什么地方去。所以路由是指线路分发&a…

MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)

前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框——属性页对话框。 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面。另外,我…

Linux用户与“最小权限”原则

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢! 作为一个Linux用户来说,我们并不需要特别关心下面的机制。但是,当我们去编写一个Linux应用程序的时候,就要注…

js urlencode 20 php,js实现php函数urlencode

本文介绍了php函数urlencode的js实现方法并比较js和php各编码函数的区别。 通常form表单的enctype类型为 application/x-www-form-urlencoded, 当表单提交后,提交的数据自动被编码, 规则为 除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两…

.NET CORE在ubuntu1604上运行

本文主要描述.NET CORE在Ubuntu上“自包含应用”不一来.NET SDK发布(类似于golang的方式) 1.用rider或者直接用dotnet new方式创建一个项目;可以看到产生了两个文件 dotnet new 会创建一个最新的 project.json 文件,其中包含生成控…

php代码审计靶场,代码审计 | Wavsep靶场审计防御

—————— 昨日回顾 ——————红日安全出品|转载请注明来源文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!(来源:红日安全)—————— ——…

Python 爬虫抓取代理IP,并检测联通性

帮朋友抓了一些代理IP,并根据测试联的通性,放在了不通的文件夹下。特将源码分享 注意: 1,环境Python3.5 2,安装BeautifulSoup4 requests 代码如下: 123456789101112131415161718192021222324252627282930313233343536…

php gps坐标转换经纬度,GPS坐标(经纬度转换xy坐标算法)

我用GPS-DG16测得某地的经纬度,然后想将该经纬度转换为80坐标系的经纬。1.看你的精度要求,如果厘米级的,则需要至少3个已知点,这3个点上要有80坐标系的经纬度,另外你再去用gps-dg16采集这三个点的wgs84经纬度&#xff…

java初始化顺序

原文地址http://blog.sina.com.cn/s/blog_4cc16fc50100bjjp.html对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通…

php审批流设计思路,审批流设计器-字段说明

1.允许转发:勾选,则在信息中心处理此审批动作对应的待处理任务时,可以将此任务转发给其他用户;不勾选,则在信息中心的待处理任务不支持转发功能,点击转交提示:审批动作设置为不允许转发。2.允许…

第一百二十六节,JavaScript,XPath操作xml节点

第一百二十六节,JavaScript,XPath操作xml节点 学习要点: 1.IE中的XPath 2.W3C中的XPath 3.XPath跨浏览器兼容 XPath是一种节点查找手段,对比之前使用标准DOM去查找XML中的节点方式,大大降低了查找难度,方便…

matlab 控制实验指导,智能控制系统-实验指导书-实验一-BP算法的MATLAB实现

实验一、BP算法的MATLAB实现一、实验目的1、了解MATLAB集成开发环境2、了解MATLAB编程基本方法3、加深对BP算法的理解和掌握二、实验内容1、MATLAB基本指令和语法。2、BP算法的MATLAB实现三、实验步骤1、熟悉MATLAB开发环境2、输入…

电力电子技术 matlab仿真指导,在_电力电子技术_课程教学中展开Matlab仿真训练_唐贤伦...

教学改革广角中国电力教育2009年10月上 总第146期 “电力电子技术”是电气工程及其自动化等专业的重要专业基础课,也是实用性、工程性和综合性很强的课程。作为自动化、电气工程及其自动化等专业的学生,学好这门课程将为后续专业课的学习和今后的工作打下…

Python补充01 序列的方法

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢! 在快速教程中,我们了解了最基本的序列(sequence)。回忆一下,序列包含有定值表(tuple)和表(list)。此外,字符串…

java socketchannel api,SocketChannel API

Netty是当前非常流行的网络通讯框架,当程序对网络数据处理时,需要保证高并发和高可靠,底层就可以用Netty支撑。本套课程详细讲解了Netty核心技术点,同时进行底层机制和源码剖析,并编写了大量的应用实例。通过学习可以快…

CLR via C# 阅读 笔记

初读  CLR Via C# 有一些 名词需要记忆: 1、CLR: Common Language Runtime (公共语言运行时) 可有多种编程语言使用的Runtime. 其核心功能是: 内存管理、 程序集加载、 安全性、 异常处理和线程同步等; 2、 Managed Module (托管模块) 托管模…

php异步轮询如何实现,深入剖析JavaScript异步之事件轮询

本篇文章给大家带来的内容是关于深入剖析JavsScript异步之事件轮询,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。JavsScript 是一门单线程的编程语言,这就意味着一个时间里只能处理一件事,也就是说 …