Cross Apply 与 Outer Apply 的区别,就像是 Inner join 与 Outer Join 的区别。
Inner Join 如果两边的表,无论是 Left Table, 或者是 Right Table, 做了限制,都会被应用到两边去,即一方的结果影响了另一方的结果。而 Out Join 则不会,即处于 Right Table 位置的表,对它做的条件限制,并不会影响 Left Table 位置上表的最后结果。
初学者肯定会被 Left Table, Right Table 给搞晕。简单理解,Inner Join, Outer Join 就是一个运算符,处于左边的就是 Left Table, 右边的就是 Right Table. 但是有例外,就是 Right Join. 它要反过来, 右边的表为 Left Table, 左边的表为 Right Table.
本质上, Left Join ,正规写法是 Left Outer Join, 左边的表(Left Table, 如果没有指定条件限制,并且不在 where 里限制右边的表 - Right Table)数据都会被作为结果返回。而 Left Outer Join 右边的表,则根据 Join 条件,只选出符合条件的记录, 不满足条件的记录,都显示 Null.
Left Table, Right Table , 还可以被称作, Preserved Table, Null-Supplying Table. Preserved Table 不受另外一个表的 Join 条件限制, 在最终结果里面,将记录全部保留作为结果,所以是 Left Table。
Cross Apply 与 Outer Apply 则有另外的术语:
Apply 运算符有内外表(表的表达式)之分。
处于图中 driver_table 位置的表,称为 Inner Table, 也称为 Driver Table. 表连接都是这张表驱动的。Correlated_table 位置的表,称为 Outer Table, 也称为 Correlated Table, 根据链接条件,选出本位置表中的数据。
当 Cross 用来形容 Apply 的时候,Correlated Table 位置上的表,会限制 Driver Table 的输出,即 Correlated Table 表中满足条件的关键字(key)必须同时在 Driver Table中也存在。而Outer Apply 则要宽容一些,在 Correlated Table 中不满足关键字(key)的记录用 Null 来替代。
SELECT BusinessEntityID
,FirstName
,LastName
,phx.PhoneNumber
FROM Person.Person per
OUTER APPLY (
SELECT PhoneNumber
FROM person.personphone ph
WHERE ph.BusinessEntityID = per.BusinessEntityID
) phx
-------------------------------------------------------------------------------
SELECT BusinessEntityID
,FirstName
,LastName
,phx.PhoneNumber
FROM Person.Person per
CROSS APPLY (
SELECT PhoneNumber
FROM person.personphone ph
WHERE ph.BusinessEntityID = per.BusinessEntityID
) phx
(经过notepad++的poorman’s T-sql formatter 插件进行了格式化)
cross apply 的结果将 293,38 这 2 条数据给滤掉了。
上次我们在文章
Cross Apply 与 Inner Join 的对抗
自动化赋值的 SQL 语句
中列出了 cross apply 的适用场景,并附上了 SQL 语句,这次我们继续基于那一次的 SQL 表结构,举个 Inner Join 的适用场景,Cross Apply 则完全失效:
SELECT count(*) AS rowseffect
FROM DimUser usr
CROSS APPLY (
SELECT TOP 3 ord.OrderID
,ord.OrderDate
,ord.OrderAmount
FROM FctOrderHeader ord
INNER JOIN FctOrderUser usra ON ord.OrderID = usra.OrderID
AND usra.UserID = usr.UserID
WHERE OrderDate BETWEEN '2015-01-01'
AND '2015-02-1'
ORDER BY OrderDate DESC
,OrderAmount DESC
) ord;
WITH BASE_QUERY
AS (
SELECT ord.OrderID
,ord.OrderDate
,ord.OrderAmount
,usr.UserID
,Row_Number() OVER (
PARTITION BY usr.UserID ORDER BY ord.OrderDate DESC
,ord.OrderAmount DESC
) AS RNK
FROM FctOrderHeader ord
INNER JOIN FctOrderUser usr ON ord.OrderID = usr.OrderID
WHERE ord.OrderDate BETWEEN '2015-01-01'
AND '2015-02-1'
)
SELECT count(*) AS rowseffect
FROM DimUser usr
INNER JOIN BASE_QUERY ord ON usr.UserID = ord.UserID
WHERE ord.RNK <= 3
SQL 写法的灵活多变,优化器给出的执行方案也就此一时,彼一时了。
需要在平时的场景中多加运用和体会!