我应该什么时候使用交叉申请内部加入?

使用CROSS APPLY的主要目的是什么?

我已经阅读(隐约地通过互联网上的帖子),如果您正在进行分区,那么在选择大数据集时, cross apply可以更有效。 (寻呼想到)

我也知道CROSS APPLY 不需要UDF作为右表。

在大多数INNER JOIN查询(一对多关系)中,我可以重写它们来使用CROSS APPLY ,但是它们总是给我等同的执行计划。

任何人都可以给我一个很好的例子,当CROSS APPLYINNER JOIN也能工作的情况下INNER JOIN吗?


编辑:

这是一个简单的例子,执行计划是完全一样的。 (给我看一个他们不同的地方, cross apply地方是更快/更有效率)

 create table Company ( companyId int identity(1,1) , companyName varchar(100) , zipcode varchar(10) , constraint PK_Company primary key (companyId) ) GO create table Person ( personId int identity(1,1) , personName varchar(100) , companyId int , constraint FK_Person_CompanyId foreign key (companyId) references dbo.Company(companyId) , constraint PK_Person primary key (personId) ) GO insert Company select 'ABC Company', '19808' union select 'XYZ Company', '08534' union select '123 Company', '10016' insert Person select 'Alan', 1 union select 'Bobby', 1 union select 'Chris', 1 union select 'Xavier', 2 union select 'Yoshi', 2 union select 'Zambrano', 2 union select 'Player 1', 3 union select 'Player 2', 3 union select 'Player 3', 3 /* using CROSS APPLY */ select * from Person p cross apply ( select * from Company c where p.companyid = c.companyId ) Czip /* the equivalent query using INNER JOIN */ select * from Person p inner join Company c on p.companyid = c.companyId 

任何人都可以给我一个很好的例子,当CROSS APPLY在INNER JOIN也能工作的情况下有所作为吗?

有关详细的性能比较,请参阅我的博客中的文章:

  • INNER JOINCROSS APPLY

CROSS APPLY在没有简单连接条件的情况下效果更好。

这个从t2t1每个记录选择3最后的记录:

 SELECT t1.*, t2o.* FROM t1 CROSS APPLY ( SELECT TOP 3 * FROM t2 WHERE t2.t1_id = t1.id ORDER BY t2.rank DESC ) t2o 

INNER JOIN条件不能简单地制定。

你可以使用CTE和窗口函数做类似的事情:

 WITH t2o AS ( SELECT t2.*, ROW_NUMBER() OVER (PARTITION BY t1_id ORDER BY rank) AS rn FROM t2 ) SELECT t1.*, t2o.* FROM t1 INNER JOIN t2o ON t2o.t1_id = t1.id AND t2o.rn <= 3 

,但是这是不太可读的,可能效率较低。

更新:

刚刚检查。

master是一个包含idPRIMARY KEY的大约20,000,000记录的表。

这个查询:

 WITH q AS ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM master ), t AS ( SELECT 1 AS id UNION ALL SELECT 2 ) SELECT * FROM t JOIN q ON q.rn <= t.id 

运行了近30秒,而这一个:

 WITH t AS ( SELECT 1 AS id UNION ALL SELECT 2 ) SELECT * FROM t CROSS APPLY ( SELECT TOP (t.id) m.* FROM master m ORDER BY id ) q 

是即时的。

cross apply有时可以让你做inner join不能做的事情。

示例(语法错误):

 select F.* from sys.objects O inner join dbo.myTableFun(O.name) F on F.schema_id= O.schema_id 

这是一个语法错误 ,因为与inner join一起使用时,表函数只能将变量或常量用作参数。 (即,表函数参数不能依赖于另一个表的列)。

然而:

 select F.* from sys.objects O cross apply ( select * from dbo.myTableFun(O.name) ) F where F.schema_id= O.schema_id 

这是合法的。

编辑:或者,更短的语法:(通过ErikE)

 select F.* from sys.objects O cross apply dbo.myTableFun(O.name) F where F.schema_id= O.schema_id 

编辑:

注:Informix 12.10 xC2 +具有横向派生表 ,Postgresql(9.3+)具有可用于类似效果的横向子查询 。

考虑你有两个表。

主表

 x------x--------------------x | Id | Name | x------x--------------------x | 1 | A | | 2 | B | | 3 | C | x------x--------------------x 

细节表

 x------x--------------------x-------x | Id | PERIOD | QTY | x------x--------------------x-------x | 1 | 2014-01-13 | 10 | | 1 | 2014-01-11 | 15 | | 1 | 2014-01-12 | 20 | | 2 | 2014-01-06 | 30 | | 2 | 2014-01-08 | 40 | x------x--------------------x-------x 

有很多情况下,我们需要用CROSS APPLY替换INNER JOIN

1.根据TOP n结果加入两个表格

考虑是否需要从Master Details table选择IdName ,并从Details table为每个Id选择最后两个日期。

 SELECT M.ID,M.NAME,D.PERIOD,D.QTY FROM MASTER M INNER JOIN ( SELECT TOP 2 ID, PERIOD,QTY FROM DETAILS D ORDER BY CAST(PERIOD AS DATE)DESC )D ON M.ID=D.ID 
  • SQL FIDDLE

上面的查询生成以下结果。

 x------x---------x--------------x-------x | Id | Name | PERIOD | QTY | x------x---------x--------------x-------x | 1 | A | 2014-01-13 | 10 | | 1 | A | 2014-01-12 | 20 | x------x---------x--------------x-------x 

看,它产生了最后两个日期的最后两个日期的结果,然后加入这些记录只在Id的外部查询,这是错误的。 要做到这一点,我们需要使用CROSS APPLY

 SELECT M.ID,M.NAME,D.PERIOD,D.QTY FROM MASTER M CROSS APPLY ( SELECT TOP 2 ID, PERIOD,QTY FROM DETAILS D WHERE M.ID=D.ID ORDER BY CAST(PERIOD AS DATE)DESC )D 
  • SQL FIDDLE

并形成以下结果。

 x------x---------x--------------x-------x | Id | Name | PERIOD | QTY | x------x---------x--------------x-------x | 1 | A | 2014-01-13 | 10 | | 1 | A | 2014-01-12 | 20 | | 2 | B | 2014-01-08 | 40 | | 2 | B | 2014-01-06 | 30 | x------x---------x--------------x-------x 

这是如何工作的。 CROSS APPLY内部的查询可以引用外部表, INNER JOIN不能这样做(它会引发编译错误)。 在找到最后两个日期时,加入是在CROSS APPLY内完成的,即WHERE M.ID=D.ID

2.当我们需要INNER JOIN功能使用功能。

当我们需要从Master表和一个function得到结果时, CROSS APPLY可以用作INNER JOIN的替换。

 SELECT M.ID,M.NAME,C.PERIOD,C.QTY FROM MASTER M CROSS APPLY dbo.FnGetQty(M.ID) C 

这是功能

 CREATE FUNCTION FnGetQty ( @Id INT ) RETURNS TABLE AS RETURN ( SELECT ID,PERIOD,QTY FROM DETAILS WHERE ID=@Id ) 
  • SQL FIDDLE

这产生了以下结果

 x------x---------x--------------x-------x | Id | Name | PERIOD | QTY | x------x---------x--------------x-------x | 1 | A | 2014-01-13 | 10 | | 1 | A | 2014-01-11 | 15 | | 1 | A | 2014-01-12 | 20 | | 2 | B | 2014-01-06 | 30 | | 2 | B | 2014-01-08 | 40 | x------x---------x--------------x-------x 

交叉应用的附加优点

APPLY可以用来替代UNPIVOTCROSS APPLYOUTER APPLY可以在这里使用,这是可以互换的。

考虑你有下面的表(名为MYTABLE )。

 x------x-------------x--------------x | Id | FROMDATE | TODATE | x------x-------------x--------------x | 1 | 2014-01-11 | 2014-01-13 | | 1 | 2014-02-23 | 2014-02-27 | | 2 | 2014-05-06 | 2014-05-30 | | 3 | NULL | NULL | x------x-------------x--------------x 

查询如下。

 SELECT DISTINCT ID,DATES FROM MYTABLE CROSS APPLY(VALUES (FROMDATE),(TODATE)) COLUMNNAMES(DATES) 
  • SQL FIDDLE

这给你带来了结果

  x------x-------------x | Id | DATES | x------x-------------x | 1 | 2014-01-11 | | 1 | 2014-01-13 | | 1 | 2014-02-23 | | 1 | 2014-02-27 | | 2 | 2014-05-06 | | 2 | 2014-05-30 | | 3 | NULL | x------x-------------x 

这是CROSS APPLY与性能有很大区别的一个例子:

使用CROSS APPLY来优化在BETWEEN条件下的连接

请注意,除了替换内部联接之外,还可以重新使用代码(如截断日期),而无需为归入标量UDF支付性能损失,例如: 使用内联UDF计算月的第三个星期三

在我看来,CROSS APPLY在处理复杂/嵌套查询中的计算字段时可以填补一定的空白,并使其更简单,更具可读性。

简单的例子:你有一个DoB,你想呈现多个年龄相关的字段,也将依赖于其他数据源(如就业),如年龄,AgeGroup,AgeAtHiring,MinimumRetirementDate等用于您的最终用户的应用程序(例如Excel数据透视表)。

选项是有限的,很少优雅:

  • JOIN子查询不能基于父查询中的数据在数据集中引入新值(它必须独立)。

  • UDF是整齐的,但速度慢,因为它们往往会阻止并行操作。 作为一个单独的实体可以是一个好的(少代码)或坏的(代码在哪里)的事情。

  • 连接表。 有时他们可以工作,但很快你就会加入大量UNION的子查询。 大混乱。

  • 创建又一个单一目的视图,假设您的计算不需要通过主要查询中途获得的数据。

  • 中介表。 是的…通常是有效的,通常是一个很好的选择,因为它们可以被索引和快速,但是由于UPDATE语句不是平行的,并且不允许级联公式(重用结果)来更新同样的说法。 有时候,你只是喜欢一口气做事。

  • 嵌套查询。 是的,在任何时候,您都可以在整个查询中放置括号,并将其用作子查询,您可以在其上操作源数据和计算字段。 但是在丑陋之前,你只能这么做。 十分难看。

  • 重复代码。 什么是3长(CASE … ELSE … END)陈述的最大价值? 这将是可读的!

    • 告诉你的客户自己计算该死的东西。

我错过了什么? 也许,请随时发表评论。 但是,嗨,CROSS APPLY在这种情况下就像是天赐之物:你只需要添加一个简单的CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl并且CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl ! 您的新领域现在已经准备好了,就像您的源数据中一直存在的一样。

通过CROSS APPLY引入的值可以…

  • 用于创建一个或多个计算字段,而不会增加组合的性能,复杂性或可读性问题
  • 像JOIN一样,几个后续的CROSS APPLY语句可以引用它们自己: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • 您可以在随后的JOIN条件下使用CROSS APPLY引入的值
  • 作为奖励,还有Table-valued功能方面

当他们做不了什么

交叉申请也适用于XML领域。 如果您希望与其他字段组合选择节点值。

例如,如果你有一个包含一些XML的表

 <root> <subnode1> <some_node value="1" /> <some_node value="2" /> <some_node value="3" /> <some_node value="4" /> </subnode1> </root> 

使用查询

 SELECT id as [xt_id] ,xmlfield.value('(/root/@attribute)[1]', 'varchar(50)') root_attribute_value ,node_attribute_value = [some_node].value('@value', 'int') ,lt.lt_name FROM dbo.table_with_xml xt CROSS APPLY xmlfield.nodes('/root/subnode1/some_node') as g ([some_node]) LEFT OUTER JOIN dbo.lookup_table lt ON [some_node].value('@value', 'int') = lt.lt_id 

会返回一个结果

 xt_id root_attribute_value node_attribute_value lt_name ---------------------------------------------------------------------- 1 test1 1 Benefits 1 test1 4 FINRPTCOMPANY 

我想这应该是可读性;)

对于阅读的人来说,CROSS APPLY将会有点独特,告诉他们UDF正在被使用,将被应用于左边表格的每一行。

当然,还有其他的限制,比其他朋友在上面发布的JOIN更适合使用CROSS APPLY。

那么我不知道这是否有资格作为使用交叉应用与内部联接的理由,但是这个查询在论坛帖子中使用交叉应用来回答,所以我不确定是否存在使用内部联接的均衡方法:

 Create PROCEDURE [dbo].[Message_FindHighestMatches] -- Declare the Topical Neighborhood @TopicalNeighborhood nchar(255) 

作为开始

 -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON Create table #temp ( MessageID int, Subjects nchar(255), SubjectsCount int ) Insert into #temp Select MessageID, Subjects, SubjectsCount From Message Select Top 20 MessageID, Subjects, SubjectsCount, (t.cnt * 100)/t3.inputvalues as MatchPercentage From #temp cross apply (select count(*) as cnt from dbo.Split(Subjects,',') as t1 join dbo.Split(@TopicalNeighborhood,',') as t2 on t1.value = t2.value) as t cross apply (select count(*) as inputValues from dbo.Split(@TopicalNeighborhood,',')) as t3 Order By MatchPercentage desc drop table #temp 

结束

在您需要子查询的列的地方,可以使用交叉应用来替换子查询

子查询

 select * from person p where p.companyId in(select c.companyId from company c where c.companyname like '%yyy%') 

在这里,我将无法选择公司表的列,所以使用交叉应用

 select P.*,T.CompanyName from Person p cross apply ( select * from Company C where p.companyid = c.companyId and c.CompanyName like '%yyy%' ) T 

这里有一篇文章解释了这一切,他们的性能差异和用法超过JOINS。

SQL Server交叉应用和外部应用联接

正如本文所建议的那样,对于正常的联接操作(INNER和CROSS),它们之间没有性能差异。

在这里输入图像描述

使用差异到达时,你必须做这样的查询:

 CREATE FUNCTION dbo.fn_GetAllEmployeeOfADepartment(@DeptID AS INT) RETURNS TABLE AS RETURN ( SELECT * FROM Employee E WHERE E.DepartmentID = @DeptID ) GO SELECT * FROM Department D CROSS APPLY dbo.fn_GetAllEmployeeOfADepartment(D.DepartmentID) 

也就是说,当你必须与功能相关联。 这不能用INNER JOIN来完成,这会给你错误“多部分标识符”D.DepartmentID“无法绑定”。 在读取每一行时,这个值被传递给函数。 听起来很酷我。 🙂

这可能是一个老问题,但我仍然喜欢CROSS APPLY的功能,以简化逻辑的重用,并为结果提供“链接”机制。

我在下面提供了一个SQL小提琴,它展示了一个简单的例子,说明如何使用CROSS APPLY在数据集上执行复杂的逻辑操作,而不会让事情变得麻烦。 从这里推断更复杂的计算并不难。

http://sqlfiddle.com/#!3/23862/2

Interesting Posts