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,一经查实,立即删除!

相关文章

vs2013和vs2010的配置

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

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…

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

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

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

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

微信小程序php实现登陆的代码,微信小程序实现微信登录

步骤:1.调用wx.login得到code返回的结果示例:{code:"051nI5Pa1XJkDs0773Pa1OWYOa1nI5PF"errMsg:"login:ok"}2.拿code换取session_key与openid这里使用服务端来请求,以php为例$code $this->input->post(code);$jso…

JavaScript 函数(作用域以及闭包)

JavaScript 函数(作用域以及闭包) ・执行环境及作用域 执行环境定义了变量或函数有权访问的其他数据。 每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量或函数都保存在这个对象中, 虽然我们编写的代码无法访问这个对…

《DSP using MATLAB》第6章开始了

看到第6章了,标记一下,全书近一半,继续加油 构建滤波器的三种元件: 下面是函数floor和size的部分帮助截图 转载于:https://www.cnblogs.com/ky027wh-sx/p/6235509.html

codeql php,使用codeql 挖掘 ofcms

前言网上关于codeql的文章并不多,国内现在对codeql的研究相对比较少,可能是因为codeql暂时没有中文文档,资料也相对较少,需要比较好的英语功底,但是我认为在随着代码量越来越多,传统的自动化漏洞挖掘工具的…

php 连接符.,PHP怎么在数字之间添加连接符

PHP实现数字之间添加连接符,我们可以通过PHP中的for循环思想来实现。这里的连接符指的是“-”符号。推荐参考:《PHP教程》那么对于新手来说,可能有一定难度。下面我们就通过简单的代码示例,给大家介绍PHP给数字之间添加连字符的实…

嵌入式linux 时间同步,解决嵌入式Linux中的时区问题

如果说让我做上层软件的工作,我做起来可以得心应手,但是让我做平台方面的工作(系统问题解决、驱动编写、软件移植等工作),确实不熟悉。所以很多问题都是摸着石头过河,没有经验。许多问题在有经验的朋友那里是小菜一碟,…

bzoj2243

2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6753 Solved: 2496[Submit][Status][Discuss]Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点…

eclipse linux windows 乱码,Ubuntu下Eclipse打开Windows下的工程文件乱码解决办法

Eclipse在Windows下默认使用的是GBK(包括GB2312)编码,而在Linux系统默认使用的是UTF-8编码,并且eclipse编码设置下拉列表不提供GBK编码可选项。如果eclipse打开工程或者文本乱码,基本可以肯定(因为常用的就这两种)这个工程/文本使用的是GBK编…

linux桌面旋转了180度,[多图]回顾每一款默认Ubuntu壁纸

每个默认的Ubuntu壁纸Ubuntu 4.10’Warty Warthog’当人们抱怨Ubuntu发行版中的“ 褐色 ”时,我经常想回到Ubuntu的第一张墙纸,以便他们的意见可以转变为“上下文”。事后看来,这只墙纸完全是一块毫无生气的带有徽标的棕色块。当然&#xff0…

【转】Apache 配置虚拟主机三种方式

Apache 配置虚拟主机三种方式 原文博客http://www.cnblogs.com/hi-bazinga/archive/2012/04/23/2466605.html一、基于IP 1. 假设服务器有个IP地址为192.168.1.10,使用ifconfig在同一个网络接口eth0上绑定3个IP: [rootlocalhost root]# ifconfig eth0:1 1…

linux weblogic 防火墙,本地访问weblogic控制台无反应,关闭linux操作系统防火墙

有时候,我们在Linux操作系统上成功启动了weblogic,也查看了7001端口的状态是开启的。但是访问weblogic控制台没有反应,也没有报错。使用 netstat -ano | grep 7001 查看端口的状态可是访问weblogic控制台,还是没有反应。我们在本地…

fedora linux搜狗输入法,在Fedora 28系统下安装搜狗输入法

以下介绍在Fedora 28系统下安装搜狗输入法,也适用在Fedora 27下的安装,亲测可以。先声明一下,在Fedora 27及以后版本中出现的输入框候选词界面变形但不影响使用。第一步:安装fzug软件源具体方法如下:1.添加 FZUG 源Fed…