使用CROSS APPLY的主要目的是什么?
我已经阅读(模糊地,通过互联网上的帖子),如果您正在进行分区,那么在选择大型数据集时, cross apply
可以更有效。 (寻找灵感)
我也知道CROSS APPLY
不需要 UDF 作为右表。
在大多数INNER JOIN
查询(一对多关系)中,我可以重写它们以使用CROSS APPLY
,但它们总是给我相同的执行计划。
任何人都可以给我一个很好的例子,当CROSS APPLY
在INNER 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 也会起作用的情况下有所作为吗?
请参阅我博客中的文章,了解详细的性能比较:
CROSS APPLY
在没有简单JOIN
条件的事情上效果更好。
这个从t2
为t1
每条记录选择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
是一个包含大约20,000,000
条记录的表,其id
为PRIMARY KEY
。
这个查询:
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
选择Id
和Name
,并从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
以上查询生成以下结果。
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
,然后仅在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
并形成以下结果。
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
)
产生了以下结果
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
可以用作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
查询如下。
SELECT DISTINCT ID,DATES
FROM MYTABLE
CROSS APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
这会带给你结果
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