关于Cross Join 和 Outer/Cross Apply

目录

先为大家准备点数据:

CREATE TABLE TA(Acol INT);
GO

INSERT INTO TA
SELECT  1 UNION ALL
SELECT  2;
GO

CREATE TABLE TB(Bcol1 INT,Bcol2 VARCHAR(10));
GO
INSERT INTO TB
SELECT -1, 'a' UNION ALL
SELECT  1, 'b' UNION ALL
SELECT  3, 'c';
GO

1. Cross Join

Cross Join,交叉联接,其实就是笛卡尔积。

select * from TA cross join TB

select * from TA,TB

的结果是一样的:

2. Cross / Outer Apply

请看下面这条语句:

select * from TA a cross apply (select * from TB where Bcol1=a.Acol) b

结果是:

是不是觉得很眼熟,是的,上面的语句和

select * from TA a inner join TB b on a .Acol = b .Bcol1

是一样的,甚至执行计划都是一样的,上面的语句完全按照inner join的思路去执行。

再看用outer apply:

select * from TA a outer apply (select * from TB where Bcol1=a.Acol) b

结果是:

是不是依然很眼熟,没错,它和

select * from TA a left join TBb on a .ACol = b .BCol1

是一样的。执行计划依然一样,上面的语句按left join的思路去执行。

既然这样,为什么还要多出一个Cross/Outer Apply呢?

3. Cross / Outer Apply的专属功能

测试数据:

CREATE TABLE tmp([str] VARCHAR(100))
GO

INSERT INTO tmp
SELECT 'I@@Love@@Coding' UNION ALL
SELECT 'This@@is@@it'
GO

假设现在有一个需求,将tmp中每一行都以@@作为分隔符拆分为列表,然后与拆分前的那一行作交叉联接(笛卡尔积)。

拆分函数网上有很多,为了节省篇幅就不放代码了,可以到我另一篇博文中看。假设此拆分函数为dbo.f_Split(@str varchar(1000),@delimiter varchar(100) = ’’ ),@str为源字符串,@delimiter为分隔符。先用常量作为函数参数来理解一下上面所说的需求:

select * from TA cross join dbo.f_Split('Xiao;;Ming',';;')

select * from TA,dbo.f_Split('Xiao;;Ming',';;')

结果:

可以看到TA中的行和表值函数返回的表作了联接。上面的cross join就可以做到。再看一下用表中的列作函数参数:

select * from tmp a cross Join dbo.f_Split(a.[str],'@@')b

结果是报错:

现在Cross Apply 的作用就真正体现出来了:

select * from tmp a cross apply dbo.f_Split(a.[str],'@@') b

结果:

4. 小结

  1. Cross Join 就是笛卡尔积;

  2. 在不涉及到表值函数的情况下,Cross Apply 和Outer Apply所起的作用与Inner Join和Left Join一样,所以一般情况下不要用Apply,这样反而容易造成不易理解的负面效应;

  3. Cross / Outer Apply 可以说完全为表值函数而生,支持了表的每一行和由此行中数据作为参数去调用表值函数而返回的结果表作交叉连接(类笛卡尔积)。看MSDN的介绍Apply的文档,没有一个实例没有用到表值函数(TF)的。