SQL Server
2005
新增
cross
apply 和
outer
apply 联接语句,增加这两个东东有啥作用呢?
我们知道有个 SQL Server
2000
中有个
cross
join
是用于交叉联接的。实际上增加
cross
apply 和
outer
apply 是用于交叉联接表值函数(返回表结果集的函数)的, 更重要的是这个函数的参数是另一个表中的字段。这个解释可能有些含混不请,请看下面的例子:
--
1. cross join 联接两个表
select
*
from
TABLE_1
as
T1
cross
join
TABLE_2
as
T2
--
2. cross join 联接表和表值函数,表值函数的参数是个“常量”
select
*
from
TABLE_1 T1
cross
join
FN_TableValue(
100
)
--
3. cross join 联接表和表值函数,表值函数的参数是“表T1中的字段”
select
*
from
TABLE_1 T1
cross
join
FN_TableValue(T1.column_a)
Msg
4104
,
Level
16
, State
1
, Line
1
The multi
-
part identifier "T1.column_a" could
not
be bound.
最后的这个查询的语法有错误。在
cross
join
时,表值函数的参数不能是表 T1 的字段, 为啥不能这样做呢?我猜可能微软当时没有加这个功能:),后来有客户抱怨后, 于是微软就增加了
cross
apply 和
outer
apply 来完善,请看
cross
apply,
outer
apply 的例子:
--
4. cross apply
select
*
from
TABLE_1 T1
cross
apply FN_TableValue(T1.column_a)
--
5. outer apply
select
*
from
TABLE_1 T1
outer
apply FN_TableValue(T1.column_a)
cross
apply 和
outer
apply 对于 T1 中的每一行都和派生表(表值函数根据T1当前行数据生成的动态结果集) 做了一个交叉联接。
cross
apply 和
outer
apply 的区别在于: 如果根据 T1 的某行数据生成的派生表为空,
cross
apply 后的结果集 就不包含 T1 中的这行数据,而
outer
apply 仍会包含这行数据,并且派生表的所有字段值都为
NULL
。
下面的例子摘自微软 SQL Server
2005
联机帮助,它很清楚的展现了
cross
apply 和
outer
apply 的不同之处:
--
cross apply
select
*
from
Departments
as
D
cross
apply fn_getsubtree(D.deptmgrid)
as
ST
deptid deptname deptmgrid empid empname mgrid lvl
--
--------- ----------- ----------- ----------- ----------- ----------- ------
1
HR
2
2
Andrew
1
0
1
HR
2
5
Steven
2
1
1
HR
2
6
Michael
2
1
2
Marketing
7
7
Robert
3
0
2
Marketing
7
11
David
7
1
2
Marketing
7
12
Ron
7
1
2
Marketing
7
13
Dan
7
1
2
Marketing
7
14
James
11
2
3
Finance
8
8
Laura
3
0
4
R
&
D
9
9
Ann
3
0
5
Training
4
4
Margaret
1
0
5
Training
4
10
Ina
4
1
(
12
row(s) affected)
--
outer apply
select
*
from
Departments
as
D
outer
apply fn_getsubtree(D.deptmgrid)
as
ST
deptid deptname deptmgrid empid empname mgrid lvl
--
--------- ----------- ----------- ----------- ----------- ----------- ------
1
HR
2
2
Andrew
1
0
1
HR
2
5
Steven
2
1
1
HR
2
6
Michael
2
1
2
Marketing
7
7
Robert
3
0
2
Marketing
7
11
David
7
1
2
Marketing
7
12
Ron
7
1
2
Marketing
7
13
Dan
7
1
2
Marketing
7
14
James
11
2
3
Finance
8
8
Laura
3
0
4
R
&
D
9
9
Ann
3
0
5
Training
4
4
Margaret
1
0
5
Training
4
10
Ina
4
1
6
Gardening
NULL
NULL
NULL
NULL
NULL
(
13
row(s) affected)
注意
outer
apply 结果集中多出的最后一行。 当 Departments 的最后一行在进行交叉联接时:deptmgrid 为
NULL
,fn_getsubtree(D.deptmgrid) 生成的派生表中没有数据,但
outer
apply 仍会包含这一行数据,这就是它和
cross
join
的不同之处。
下面是完整的测试代码,你可以在 SQL Server
2005
联机帮助上找到:
--
create Employees table and insert values
IF
OBJECT_ID
(
'
Employees
'
)
IS
NOT
NULL
DROP
TABLE
Employees
GO
CREATE
TABLE
Employees
(
empid
INT
NOT
NULL
,
mgrid
INT
NULL
,
empname
VARCHAR
(
25
)
NOT
NULL
,
salary
MONEY
NOT
NULL
)
GO
IF
OBJECT_ID
(
'
Departments
'
)
IS
NOT
NULL
DROP
TABLE
Departments
GO
--
create Departments table and insert values
CREATE
TABLE
Departments
(
deptid
INT
NOT
NULL
PRIMARY
KEY
,
deptname
VARCHAR
(
25
)
NOT
NULL
,
deptmgrid
INT
)
GO
--
fill datas
INSERT
INTO
employees
VALUES
(
1
,
NULL
,
'
Nancy
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
2
,
1
,
'
Andrew
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
3
,
1
,
'
Janet
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
4
,
1
,
'
Margaret
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
5
,
2
,
'
Steven
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
6
,
2
,
'
Michael
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
7
,
3
,
'
Robert
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
8
,
3
,
'
Laura
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
9
,
3
,
'
Ann
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
10
,
4
,
'
Ina
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
11
,
7
,
'
David
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
12
,
7
,
'
Ron
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
13
,
7
,
'
Dan
'
,
00.00
)
INSERT
INTO
employees
VALUES
(
14
,
11
,
'
James
'
,
00.00
)
INSERT
INTO
departments
VALUES
(
1
,
'
HR
'
,
2
)
INSERT
INTO
departments
VALUES
(
2
,
'
Marketing
'
,
7
)
INSERT
INTO
departments
VALUES
(
3
,
'
Finance
'
,
8
)
INSERT
INTO
departments
VALUES
(
4
,
'
R&D
'
,
9
)
INSERT
INTO
departments
VALUES
(
5
,
'
Training
'
,
4
)
INSERT
INTO
departments
VALUES
(
6
,
'
Gardening
'
,
NULL
)
GO
--
SELECT * FROM departments
--
table-value function
IF
OBJECT_ID
(
'
fn_getsubtree
'
)
IS
NOT
NULL
DROP
FUNCTION
fn_getsubtree
GO
CREATE
FUNCTION
dbo.fn_getsubtree(
@empid
AS
INT
)
RETURNS
TABLE
AS
RETURN
(
WITH
Employees_Subtree(empid, empname, mgrid, lvl)
AS
(
--
Anchor Member (AM)
SELECT
empid, empname, mgrid,
0
FROM
employees
WHERE
empid
=
@empid
UNION
ALL
--
Recursive Member (RM)
SELECT
e.empid, e.empname, e.mgrid, es.lvl
+
1
FROM
employees
AS
e
join
employees_subtree
AS
es
ON
e.mgrid
=
es.empid
)
SELECT
*
FROM
Employees_Subtree
)
GO
--
cross apply query
SELECT
*
FROM
Departments
AS
D
CROSS
APPLY fn_getsubtree(D.deptmgrid)
AS
ST
--
outer apply query
SELECT
*
FROM
Departments
AS
D
OUTER APPLY fn_getsubtree(D.deptmgrid) AS ST
下面还有一个小例子,根据小例子可以将cross apply /outer apply 分别换成inner join /left join,结果是一样的,但是cross apply之后一般是带函数.
inner join之后一般是带表.
create
table
#T(姓名
varchar
(10))
insert
into
#T
values
(
'张三'
)
insert
into
#T
values
(
'李四'
)
insert
into
#T
values
(
NULL
)
create
table
#T2(姓名
varchar
(10) , 课程
varchar
(10) , 分数
int
)
insert
into
#T2
values
(
'张三'
,
'语文'
, 74)
insert
into
#T2
values
(
'张三'
,
'数学'
, 83)
insert
into
#T2
values
(
'张三'
,
'物理'
, 93)
insert
into
#T2
values
(
NULL
,
'数学'
, 50)
go
select
*
from
#T a
cross
apply
(
select
课程,分数
from
#t2
where
姓名=a.姓名) b
/*
select
a.
*,b.课程,b.分数
from
#T a
inner
join
#t2
b
where
a.姓名=b.姓名
*/
/*
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
(3 行受影响)
*/
select
*
from
#T a
outer
apply
(
select
课程,分数
from
#t2
where
姓名=a.姓名) b
/*
select
a.
*,b.课程,b.分数
from
#T a
left
join #t2
b
where
a.姓名=b.姓名
*/
/*
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四
NULL
NULL
NULL
NULL
NULL
(5 行受影响)