Jef*_*ang 890 sql t-sql sql-server performance cross-apply
使用CROSS APPLY的主要目的是什么?
我已经阅读(模糊地,通过互联网上的帖子),cross apply如果你正在进行分区,那么在选择大型数据集时可以更有效率.(寻找灵感)
我也知道CROSS APPLY 不需要UDF作为右表.
在大多数INNER JOIN查询(一对多关系)中,我可以重写它们以供使用CROSS APPLY,但它们总是给我相同的执行计划.
任何人都可以给我一个很好的例子,告诉我什么时候能有效的CROSS APPLY情况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
Run Code Online (Sandbox Code Playgroud)
Qua*_*noi 641
任何人都可以给我一个很好的例子,当CROSS APPLY在INNER JOIN也会起作用的情况下有所作为吗?
请参阅我博客中的文章,了解详细的性能比较:
CROSS APPLY在没有简单JOIN条件的事情上做得更好.
这个选择3来自t2以下每条记录的最后记录t1:
SELECT t1.*, t2o.*
FROM t1
CROSS APPLY
(
SELECT TOP 3 *
FROM t2
WHERE t2.t1_id = t1.id
ORDER BY
t2.rank DESC
) t2o
Run Code Online (Sandbox Code Playgroud)
它不容易用INNER JOIN条件配制.
您可以使用CTE's&window函数执行类似的操作:
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
Run Code Online (Sandbox Code Playgroud)
,但这不太可读,可能效率较低.
更新:
刚检查过.
master是的有关表的20,000,000一个记录PRIMARY KEY上id.
这个查询:
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
Run Code Online (Sandbox Code Playgroud)
运行几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
Run Code Online (Sandbox Code Playgroud)
是即时的.
nur*_*tin 191
cross apply有时候你可以做你不能做的事情inner join.
示例(语法错误):
select F.* from sys.objects O
inner join dbo.myTableFun(O.name) F
on F.schema_id= O.schema_id
Run Code Online (Sandbox Code Playgroud)
这是一个语法错误,因为与inner join表函数一起使用时,只能将变量或常量作为参数.(即,表函数参数不能依赖于另一个表的列.)
然而:
select F.* from sys.objects O
cross apply ( select * from dbo.myTableFun(O.name) ) F
where F.schema_id= O.schema_id
Run Code Online (Sandbox Code Playgroud)
这是合法的.
编辑: 或者,更短的语法:(由ErikE)
select F.* from sys.objects O
cross apply dbo.myTableFun(O.name) F
where F.schema_id= O.schema_id
Run Code Online (Sandbox Code Playgroud)
编辑:
注意:Informix 12.10 xC2 +具有横向派生表,Postgresql(9.3+)具有横向子查询,可用于类似的效果.
Sar*_*avu 160
考虑你有两张桌子.
主表
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
Run Code Online (Sandbox Code Playgroud)
详情表
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
Run Code Online (Sandbox Code Playgroud)
在有些情况下,我们需要更换很多情况下INNER JOIN用CROSS APPLY.
1.根据TOP n结果加入两个表
试想,如果我们需要选择Id并Name从Master和最后两个日期为每个Id从Details table.
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
Run Code Online (Sandbox Code Playgroud)
以上查询生成以下结果.
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
Run Code Online (Sandbox Code Playgroud)
看,它生成了最后两个日期的最后两个日期的结果Id,然后仅在外部查询中加入这些记录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
Run Code Online (Sandbox Code Playgroud)
并形成以下结果.
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
Run Code Online (Sandbox Code Playgroud)
这是它的工作原理.里面的查询CROSS APPLY可以引用外部表,在那里INNER JOIN不能这样做(它抛出编译错误).当找到最后两个日期时,加入在内部完成,CROSS APPLY即WHERE M.ID=D.ID.
2.当我们需要INNER JOIN使用功能的功能时.
CROSS APPLYINNER JOIN当我们需要从Master表和a 获得结果时,可以用作替代function.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
CROSS APPLY dbo.FnGetQty(M.ID) C
Run Code Online (Sandbox Code Playgroud)
这是功能
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=@Id
)
Run Code Online (Sandbox Code Playgroud)
产生了以下结果
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
Run Code Online (Sandbox Code Playgroud)
交叉应用的额外优势
APPLY可以用作替代品UNPIVOT.在这里使用CROSS APPLY或OUTER 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
Run Code Online (Sandbox Code Playgroud)
查询如下.
SELECT DISTINCT ID,DATES
FROM MYTABLE
CROSS APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
Run Code Online (Sandbox Code Playgroud)
这会带给你结果
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
Run Code Online (Sandbox Code Playgroud)
mto*_*one 40
在我看来,当使用复杂/嵌套查询中的计算字段时,CROSS APPLY可以填补一定的空白,并使它们更简单,更易读.
简单示例:您有一个DoB,并且您想要呈现多个与年龄相关的字段,这些字段也将依赖于其他数据源(如就业),如Age,AgeGroup,AgeAtHiring,MinimumRetirementDate等,以便在最终用户应用程序中使用(例如,Excel PivotTables).
选项有限,很少优雅:
JOIN子查询无法根据父查询中的数据在数据集中引入新值(它必须独立存在).
UDF很整洁,但速度慢,因为它们往往会阻止并行操作.并且作为一个单独的实体可以是一个好的(更少的代码)或一个坏的(代码在哪里)的东西.
连接表.有时他们可以工作,但很快就会加入大量UNION的子查询.大混乱.
创建另一个单用途视图,假设您的计算不需要在主查询中途获得的数据.
中介表.是的......这通常是有效的,并且通常是一个很好的选择,因为它们可以被索引和快速,但由于UPDATE语句不是并行的,并且不允许级联公式(重用结果)来更新内部的几个字段,性能也会下降同样的声明.有时你只是喜欢一次性做事.
嵌套查询.是的,您可以在任何时候将括号放在整个查询上,并将其用作子查询,您可以在其上操作源数据和计算字段.但是你只能在丑陋之前这么做.十分难看.
重复代码.3长(CASE ... ELSE ... END)陈述的最大价值是什么?那将是可读的!
我错过了什么?也许,请随意发表评论.但是,嘿,CROSS APPLY在这种情况下就像天赐之物:你只需添加一个简单的CROSS APPLY (select tbl.value + 1 as someFormula) as crossTblvo!您的新字段现在可以使用了,就像您在源数据中一直存在的那样.
通过CROSS APPLY引入的价值可以......
CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2Dang,他们无能为力!
小智 14
交叉应用也适用于XML字段.如果要与其他字段一起选择节点值.
例如,如果您有一个包含某些xml的表
Run Code Online (Sandbox Code Playgroud)<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
Run Code Online (Sandbox Code Playgroud)
将返回结果
xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1 test1 1 Benefits
1 test1 4 FINRPTCOMPANY
Run Code Online (Sandbox Code Playgroud)
小智 12
从技术上来说,这已经得到了很好的回答,但是让我举一个具体的例子说明它是多么有用:
假设您有两个表,客户表和订单。客户有很多订单。
我想创建一个视图,为我提供有关客户以及他们最近完成的订单的详细信息。仅使用JOINS,这将需要一些自联接和聚合,这并不理想。但是使用Cross Apply,它超级简单:
SELECT *
FROM Customer
CROSS APPLY (
SELECT TOP 1 *
FROM Order
WHERE Order.CustomerId = Customer.CustomerId
ORDER BY OrderDate DESC
) T
Run Code Online (Sandbox Code Playgroud)
.sql这是我为自己编写的一个简短教程,可以保存在文件中并在 SSMS 中执行,以快速刷新我对如何CROSS APPLY工作以及何时使用它的记忆:
-- Here's the key to understanding CROSS APPLY: despite the totally different name, think of it as being like an advanced 'basic join'.
-- A 'basic join' gives the Cartesian product of the rows in the tables on both sides of the join: all rows on the left joined with all rows on the right.
-- The formal name of this join in SQL is a CROSS JOIN. You now start to understand why they named the operator CROSS APPLY.
-- Given the following (very) simple tables and data:
CREATE TABLE #TempStrings ([SomeString] [nvarchar](10) NOT NULL);
CREATE TABLE #TempNumbers ([SomeNumber] [int] NOT NULL);
CREATE TABLE #TempNumbers2 ([SomeNumber] [int] NOT NULL);
INSERT INTO #TempStrings VALUES ('111'); INSERT INTO #TempStrings VALUES ('222');
INSERT INTO #TempNumbers VALUES (111); INSERT INTO #TempNumbers VALUES (222);
INSERT INTO #TempNumbers2 VALUES (111); INSERT INTO #TempNumbers2 VALUES (222); INSERT INTO #TempNumbers2 VALUES (222);
-- Basic join is like CROSS APPLY; 2 rows on each side gives us an output of 4 rows, but 2 rows on the left and 0 on the right gives us an output of 0 rows:
SELECT
st.SomeString, nbr.SomeNumber
FROM -- Basic join ('CROSS JOIN')
#TempStrings st, #TempNumbers nbr
-- Note: this also works:
--#TempStrings st CROSS JOIN #TempNumbers nbr
-- Basic join can be used to achieve the functionality of INNER JOIN by first generating all row combinations and then whittling them down with a WHERE clause:
SELECT
st.SomeString, nbr.SomeNumber
FROM -- Basic join ('CROSS JOIN')
#TempStrings st, #TempNumbers nbr
WHERE
st.SomeString = nbr.SomeNumber
-- However, for increased readability, the SQL standard introduced the INNER JOIN ... ON syntax for increased clarity; it brings the columns that two tables are
-- being joined on next to the JOIN clause, rather than having them later on in the WHERE clause. When multiple tables are being joined together, this makes it
-- much easier to read which columns are being joined on which tables; but make no mistake, the following syntax is *semantically identical* to the above syntax:
SELECT
st.SomeString, nbr.SomeNumber
FROM -- Inner join
#TempStrings st INNER JOIN #TempNumbers nbr ON st.SomeString = nbr.SomeNumber
-- Because CROSS APPLY is generally used with a subquery, the subquery's WHERE clause will appear next to the join clause (CROSS APPLY), much like the aforementioned
-- 'ON' keyword appears next to the INNER JOIN clause. In this sense, then, CROSS APPLY combined with a subquery that has a WHERE clause is like an INNER JOIN with
-- an ON keyword, but more powerful because it can be used with subqueries (or table-valued functions, where said WHERE clause can be hidden inside the function).
SELECT
st.SomeString, nbr.SomeNumber
FROM
#TempStrings st CROSS APPLY (SELECT * FROM #TempNumbers tempNbr WHERE st.SomeString = tempNbr.SomeNumber) nbr
-- CROSS APPLY joins in the same way as a CROSS JOIN, but what is joined can be a subquery or table-valued function. You'll still get 0 rows of output if
-- there are 0 rows on either side, and in this sense it's like an INNER JOIN:
SELECT
st.SomeString, nbr.SomeNumber
FROM
#TempStrings st CROSS APPLY (SELECT * FROM #TempNumbers tempNbr WHERE 1 = 2) nbr
-- OUTER APPLY is like CROSS APPLY, except that if one side of the join has 0 rows, you'll get the values of the side that has rows, with NULL values for
-- the other side's columns. In this sense it's like a FULL OUTER JOIN:
SELECT
st.SomeString, nbr.SomeNumber
FROM
#TempStrings st OUTER APPLY (SELECT * FROM #TempNumbers tempNbr WHERE 1 = 2) nbr
-- One thing CROSS APPLY makes it easy to do is to use a subquery where you would usually have to use GROUP BY with aggregate functions in the SELECT list.
-- In the following example, we can get an aggregate of string values from a second table based on matching one of its columns with a value from the first
-- table - something that would have had to be done in the ON clause of the LEFT JOIN - but because we're now using a subquery thanks to CROSS APPLY, we
-- don't need to worry about GROUP BY in the main query and so we don't have to put all the SELECT values inside an aggregate function like MIN().
SELECT
st.SomeString, nbr.SomeNumbers
FROM
#TempStrings st CROSS APPLY (SELECT SomeNumbers = STRING_AGG(tempNbr.SomeNumber, ', ') FROM #TempNumbers2 tempNbr WHERE st.SomeString = tempNbr.SomeNumber) nbr
-- ^ First the subquery is whittled down with the WHERE clause, then the aggregate function is applied with no GROUP BY clause; this means all rows are
-- grouped into one, and the aggregate function aggregates them all, in this case building a comma-delimited string containing their values.
DROP TABLE #TempStrings;
DROP TABLE #TempNumbers;
DROP TABLE #TempNumbers2;
Run Code Online (Sandbox Code Playgroud)
小智 8
这里有一篇文章解释了这一切,以及它们与 JOINS 的性能差异和用法。
SQL Server 通过 JOINS 进行交叉应用和外部应用
正如本文所建议的,对于正常的连接操作(INNER AND 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)
Run Code Online (Sandbox Code Playgroud)
也就是说,当你必须与功能联系起来时。使用 INNER JOIN 无法完成此操作,否则会出现错误“无法绑定多部分标识符“D.DepartmentID””。这里,当读取每一行时,值被传递给函数。对我来说听起来很酷。:)
小智 7
交叉应用可用于替换需要子查询列的子查询
子查询
select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')
Run Code Online (Sandbox Code Playgroud)
在这里,我将无法选择公司表的列,因此,使用交叉申请
select P.*,T.CompanyName
from Person p
cross apply (
select *
from Company C
where p.companyid = c.companyId and c.CompanyName like '%yyy%'
) T
Run Code Online (Sandbox Code Playgroud)
我想应该是可读性;)
对于那些要告诉他们正在使用UDF的人来说,CROSS APPLY在某种程度上将是唯一的,它将应用于左侧表格的每一行。
当然,还有其他一些限制,那就是使用CROSS APPLY比使用其他好友在上面发布的JOIN更好。
APPLY 运算符的本质是允许 FROM 子句中运算符的左侧和右侧之间存在关联。
与 JOIN 相比,不允许输入之间存在关联。
谈到 APPLY 运算符中的相关性,我的意思是在右侧我们可以输入:
两者都可以返回多列和行。