不带子查询的外部应用

OUTER apply without subquery

我在 SQL 服务器中浏览了一篇关于 CROSS APPLYOUTER APPLY 的文章。以下 table 用于说明两者。

员工table:

EmployeeID  FirstName   LastName    DepartmentID

1           Orlando     Gee         1
2           Keith       Harris      2
3           Donna       Carreras    3
4           Janet       Gates       3

部门table:

DepartmentID    Name
1               Engineering
2               Administration
3               Sales
4               Marketing
5               Finance

我知道 OUTER APPLY 类似于 LEFT OUTER JOIN. 但是当我在 table 之间应用 OUTER APPLY 时,

select * from Department e
outer apply
Employee d
where d.DepartmentID = e.DepartmentID

我得到以下结果(与 INNER JOIN 结果相同)

DepartmentID    Name           EmployeeID   FirstName   LastName    DepartmentID
1               Engineering     1           Orlando     Gee          1
2               Administration  2           Keith       Harris       2
3               Sales           3           Donna       Carreras     3
3               Sales           4           Janet       Gates        3

当我在 table 之间应用 OUTER APPLY 时,如下所示(使用 right table 作为子查询)。

select * from Department e
outer apply
(
select * from
Employee d
where d.DepartmentID = e.DepartmentID
)a

我得到以下结果(与 LEFT OUTER JOIN 结果相同)

DepartmentID    Name           EmployeeID   FirstName   LastName    DepartmentID
1               Engineering     1           Orlando     Gee          1
2               Administration  2           Keith       Harris       2
3               Sales           3           Donna       Carreras     3
3               Sales           4           Janet       Gates        3
4               Marketing       NULL        NULL        NULL         NULL
5               Finance         NULL        NULL        NULL         NULL

有人可以解释为什么这两个查询给出不同的 outputs 吗?

我认为理解这一点的关键是查看此查询的输出:

select * from Department e
outer apply
Employee d
--where d.DepartmentID = e.DepartmentID

它简单地给出了两个表的笛卡尔积:

DepartmentID    Name            EmployeeID  FirstName   LastName    DepartmentID
--------------------------------------------------------------------------------------
1               Engineering     1           Orlando     Gee         1
2               Administration  1           Orlando     Gee         1
3               Sales           1           Orlando     Gee         1
4               Marketing       1           Orlando     Gee         1
5               Finance         1           Orlando     Gee         1
1               Engineering     2           Keith       Harris      2
2               Administration  2           Keith       Harris      2
3               Sales           2           Keith       Harris      2
4               Marketing       2           Keith       Harris      2
5               Finance         2           Keith       Harris      2
1               Engineering     3           Donna       Carreras    3
2               Administration  3           Donna       Carreras    3
3               Sales           3           Donna       Carreras    3
4               Marketing       3           Donna       Carreras    3
5               Finance         3           Donna       Carreras    3
1               Engineering     4          Janet        Gates       3   
2               Administration  4          Janet        Gates       3   
3               Sales           4          Janet        Gates       3   
4               Marketing       4          Janet        Gates       3   
5               Finance         4          Janet        Gates       3   

现在,当您在 where 子句 where d.DepartmentID = e.DepartmentID 中添加回来时,您消除了其中的大部分行:

DepartmentID    Name            EmployeeID  FirstName   LastName    DepartmentID
--------------------------------------------------------------------------------------
1               Engineering     1           Orlando     Gee         1
2               Administration  2           Keith       Harris      2
3               Sales           3           Donna       Carreras    3
3               Sales           4          Janet        Gates       3   

此查询在语义上等同于:

SELECT * FROM Department e
CROSS JOIN Employee d
WHERE d.DepartmentID = e.DepartmentID;

等于:

SELECT * FROM Department e
INNER JOIN Employee d
ON d.DepartmentID = e.DepartmentID;

因此,即使您有一个 OUTER APPLY,您的 where 子句也会将其变成 INNER JOIN,从而删除没有员工的部门。

您可以在下面看到您的第一个查询在 Department 和 employee 之间应用的计划。由于您的 where 子句,它被转换为内部连接。

第二个查询的执行计划显示部门和员工之间的左外连接 table。在您对每个部门的第二个查询中,如果没有员工在场,您的检查员工子查询将 return 为空。

但是在第一个查询中,由于您的 where 子句,具有 NULL 值的行被删除了。

图中'e'和'd'分别是employeedepartmenttables.

尽管您可以使用 apply operator this is not what it was designed for. The primary purpose, from MSDN:

加入 tables

The APPLY operator allows you to invoke a table-valued function for each row returned by an outer table expression of a query.

顾名思义; table 值函数是 return 是 table 的任何函数。这是一个简单的函数:

-- Function that takes a number, adds one and returns the result.
CREATE FUNCTION AddOne 
    (
        @StartNumber INT
    )
RETURNS TABLE
AS
RETURN
    (
        SELECT
            @StartNumber + 1 AS [Result]
    )
GO

下面是一些示例数据:

-- Sample data.
DECLARE @SampleTable TABLE
    (
        Number INT
    )
;

INSERT INTO @SampleTable
    (
        Number
    )
VALUES
    (1),
    (2),
    (3)
;

将函数应用到我们的 table,像这样:

-- Using apply.
SELECT
    st.Number,
    ad.Result
FROM
    @SampleTable AS st
        CROSS APPLY AddOne(st.Number) AS ad
;

Returns:

Number  Result
1       2
2       3
3       4

Robert Sheldon 的这篇博文更详细地解释了上述内容。

apply 运算符也可以与 table value constructor 结合使用另一种方法 return 完全相同的结果:

-- Using TVC.
SELECT
    st.Number,
    ad.Result
FROM
    @SampleTable AS st
        CROSS APPLY 
            (
                VALUES  
                    (st.Number + 1)
            ) AS ad(Result)
;

这项强大的技术允许您对数据执行计算,并为结果指定别名。

当涉及到应用运算符时,这个答案几乎没有触及表面。它有更多的技巧。我强烈建议进一步研究。