真实生活的例子,何时在SQL中使用OUTER / CROSS APPLY
我一直在和一位同事一起研究CROSS / OUTER APPLY
,我们正在努力寻找使用它们的真实例子。
我花了很多时间看我什么时候应该通过内部联接使用交叉应用? 和谷歌搜索,但主要(唯一)的例子似乎很奇怪(使用表中的行数来确定从另一个表中选择多少行)。
我认为这种情况可能会从OUTER APPLY
受益:
联系人表(每个联系人包含1条记录)通讯条目表(可包含n个电话,传真,每个联系人的电子邮件)
但是使用子查询,公用表表达式,带RANK()
和OUTER APPLY
OUTER JOIN
似乎都表现平等。 我猜这意味着这种情况不适用于APPLY
。
请分享一些真实生活的例子,并帮助解释功能!
APPLY
一些用途是......
1)每组查询的前N个(对于某些基数可以更高效)
SELECT pr.name,
pa.name
FROM sys.procedures pr
OUTER APPLY (SELECT TOP 2 *
FROM sys.parameters pa
WHERE pa.object_id = pr.object_id
ORDER BY pr.name) pa
ORDER BY pr.name,
pa.name
2)为外部查询中的每一行调用一个表值函数
SELECT *
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle)
3)重新使用列别名
SELECT number,
doubled_number,
doubled_number_plus_one
FROM master..spt_values
CROSS APPLY (SELECT 2 * CAST(number AS BIGINT)) CA1(doubled_number)
CROSS APPLY (SELECT doubled_number + 1) CA2(doubled_number_plus_one)
4)重新指定多个列组
假设1NF违反表格结构....
CREATE TABLE T
(
Id INT PRIMARY KEY,
Foo1 INT, Foo2 INT, Foo3 INT,
Bar1 INT, Bar2 INT, Bar3 INT
);
使用2008+ VALUES
语法的示例。
SELECT Id,
Foo,
Bar
FROM T
CROSS APPLY (VALUES(Foo1, Bar1),
(Foo2, Bar2),
(Foo3, Bar3)) V(Foo, Bar);
在2005年, UNION ALL
可以用来代替。
SELECT Id,
Foo,
Bar
FROM T
CROSS APPLY (SELECT Foo1, Bar1
UNION ALL
SELECT Foo2, Bar2
UNION ALL
SELECT Foo3, Bar3) V(Foo, Bar);
在各种情况下,您无法避免CROSS APPLY
或OUTER APPLY
。
考虑你有两张桌子。
主表
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
细节表
x------x--------------------x-------x
| Id | PERIOD | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 1 | 2014-01-11 | 15 |
| 1 | 2014-01-12 | 20 |
| 2 | 2014-01-06 | 30 |
| 2 | 2014-01-08 | 40 |
x------x--------------------x-------x
交叉申请
有很多情况下我们需要用CROSS APPLY
替换INNER JOIN
。
1.如果我们想要使用INNER JOIN
功能在TOP n
结果中加入2个表格
考虑我们是否需要从Master
Details table
选择Id
和Name
,并从Details table
为每个Id
选择最后两个日期。
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
INNER JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
以上查询生成以下结果。
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
x------x---------x--------------x-------x
请参阅:它使用最后两个日期的Id
生成最后两个日期的结果,然后仅在Id
外部查询中加入这些记录,这是错误的。 为了完成这个,我们需要使用CROSS APPLY
。
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
CROSS APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
并形成他以下的结果。
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
x------x---------x--------------x-------x
这是工作。 CROSS APPLY
内部的查询可以引用外部表, INNER JOIN
不能这样做(抛出编译错误)。 找到最后两个日期时,加入是在CROSS APPLY
内完成的,即WHERE M.ID=D.ID
2.当我们需要使用函数的INNER JOIN
功能时。
当我们需要从Master
表和一个function
获得结果时, CROSS APPLY
可以用作INNER JOIN
的替换。
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
CROSS APPLY dbo.FnGetQty(M.ID) C
这是功能
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=@Id
)
这产生了以下结果
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
x------x---------x--------------x-------x
外部应用
1.如果我们想要使用LEFT JOIN
功能在TOP n
结果中加入2个表格
考虑我们是否需要从Master
表中选择ID和名称,并从Details
表中为每个ID选择最后两个日期。
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
LEFT JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
这形成以下结果
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | NULL | NULL |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
这会带来错误的结果,即,它会从只带来最新的两个日期数据Details
表无论Id
,即使我们与加盟Id
。 所以正确的解决方案是使用OUTER APPLY
。
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
OUTER APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
这形成了以下期望的结果
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
2.当我们需要使用functions
LEFT JOIN
functions
。
当我们需要从Master
表格和function
获得结果时, OUTER APPLY
可以用作LEFT JOIN
的替换。
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
功能在这里。
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=@Id
)
这产生了以下结果
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
CROSS APPLY
和OUTER APPLY
共同特点
CROSS APPLY
或OUTER APPLY
可用于在未转换时保留NULL
值,这些值可互换。
考虑你有下面的表格
x------x-------------x--------------x
| Id | FROMDATE | TODATE |
x------x-------------x--------------x
| 1 | 2014-01-11 | 2014-01-13 |
| 1 | 2014-02-23 | 2014-02-27 |
| 2 | 2014-05-06 | 2014-05-30 |
| 3 | NULL | NULL |
x------x-------------x--------------x
当您使用UNPIVOT
将FROMDATE
AND TODATE
为一列时,它将默认消除NULL
值。
SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
产生下面的结果。 请注意,我们错过了Id
号码3
的记录
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
x------x-------------x
在这种情况下, CROSS APPLY
或OUTER APPLY
将会有用
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
形成以下结果并保留其值为3
Id
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
| 3 | NULL |
x------x-------------x
一个真实的例子是如果你有一个调度器,并想看看每个调度任务的最新日志条目是什么。
select t.taskName, lg.logResult, lg.lastUpdateDate
from task t
cross apply (select top 1 taskID, logResult, lastUpdateDate
from taskLog l
where l.taskID = t.taskID
order by lastUpdateDate desc) lg
链接地址: http://www.djcxy.com/p/86207.html
上一篇: Real life example, when to use OUTER / CROSS APPLY in SQL
下一篇: Equivalent of getLong for a TimestampType/java.sql.Timestamp?