学而实习之 不亦乐乎

SQL Server中APPLY运算符的用法以及CROSS APPLY和OUTER APPLY的区别

2019-01-23 17:42:02

关于 APPLY 运算符微软的官方文档的解释如下:
使用 APPLY 运算符可以为实现查询操作的外部表表达式返回的每个行调用表值函数。表值函数作为右输入,外部表表达式作为左输入。通过对右输入求值来获得左输入每一行的计算结果,生成的行被组合起来作为最终输出。APPLY 运算符生成的列的列表是左输入中的列集,后跟右输入返回的列的列表。

APPLY 有两种形式:CROSS APPLY 和 OUTER APPLY。CROSS APPLY 仅返回外部表中通过表值函数生成结果集的行。OUTER APPLY 既返回生成结果集的行,也返回不生成结果集的行,其中表值函数生成的列中的值为 NULL。

以上说的通俗一些就是:
APPLY运算符把左表表达式中的每一行调用右输入,这里说到右输入是表值函数(即函数返回一张表),表值函数的参数是从左表表达式中得到的。
有时候我们右输入直接用一个SELECT语句来表示,这样做让我迷糊了好长时间,实际上用表值函数更让人容易理解APPLY的用法。
APPLY有两种形式,一个是OUTER APPLY,一个是CROSS APPLY,区别在于指定OUTER,意味着结果集中将包含使右表表达式为空的左表表达式中的行,而指定CROSS,结果集中不包含使右表表达式为空的左表表达式中的行。

1.APPLY运算符使用方法:(可参考微软的文档)

CREATE TABLE Employees
(
    empid int NOT NULL
    ,department int NULL
    ,empname varchar(25) NOT NULL
);
GO

INSERT INTO Employees VALUES(1 ,1,'Nancy');
INSERT INTO Employees VALUES(2 ,1,'Andrew');
INSERT INTO Employees VALUES(3 ,2,'Janet');
INSERT INTO Employees VALUES(4 ,2,'Margaret');
INSERT INTO Employees VALUES(5 ,3,'Steven');
INSERT INTO Employees VALUES(6 ,3,'Michael');
INSERT INTO Employees VALUES(7 ,4,'Robert');
INSERT INTO Employees VALUES(8 ,4 ,'Laura');
INSERT INTO Employees VALUES(9 ,5 ,'Ann');
INSERT INTO Employees VALUES(10,5 ,'Ina');
GO

CREATE TABLE Departments
(
    did INT NOT NULL PRIMARY KEY
    ,dname VARCHAR(25) NOT NULL
);
GO

INSERT INTO Departments VALUES(1,'HR');
INSERT INTO Departments VALUES(2,'Marketing');
INSERT INTO Departments VALUES(3,'Finance');
INSERT INTO Departments VALUES(4,'Training');

方法一
1.首先需要创建表值函数fn_yourFunction。

CREATE FUNCTION dbo.fn_yourFunction(@did AS INT) 
    RETURNS @TREE TABLE
(
    empid   INT NOT NULL
    ,department INT NOT NULL
    ,empname VARCHAR(25) NOT NULL
)
AS
BEGIN
  WITH Employees_Subtree(empid,department,empname)
  AS
  ( 
    -- Anchor Member (AM)
    SELECT empid, department, empname
    FROM Employees
    WHERE department = @did
  )
  INSERT INTO @TREE
    SELECT * FROM Employees_Subtree;

  RETURN
END
GO

2.执行语句:

SELECT D.did, D.dname, E.empid, E.department 
FROM Departments AS D
CROSS APPLY fn_yourFunction(D.did) AS E;

方法二
当然,当逻辑比较简单时,有时为了方便,我们更倾向于用一个SELECT语句来做为右输入,这样更简单一些。
如:

SELECT D.did, D.dname, E.empid, E.department 
FROM Departments AS D
OUTER APPLY (SELECT * FROM Employees WHERE department=D.did) E

2. CROSS APPLY 和 OUTER APPLY 的区别
上面关于二者的不同已经说的很清楚了,下面只做一个实例看一下。
分别执行:

SELECT * FROM Employees AS E CROSS APPLY (SELECT * FROM Departments WHERE did=E.department) D
SELECT * FROM Employees AS E OUTER APPLY (SELECT * FROM Departments WHERE did=E.department) D

结果如下图所示:

这样很清楚的看到了它们的不同之处。