Um
LEFT JOIN
deve ser substituído por OUTER APPLY
nas seguintes situações. 1. Se quisermos juntar duas tabelas com base em
TOP n
resultados Considere se precisamos selecionar
Id
e Name
de Master
e duas últimas datas para cada Id
de Details
tabela. 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
que forma o seguinte resultado
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
Isso trará resultados errados, ou seja, trará apenas as últimas duas datas de
Details
tabela independente de Id
mesmo que nos juntemos com Id
. Portanto, a solução adequada é usar 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
Aqui está o trabalho:Em
LEFT JOIN
, TOP 2
as datas serão unidas ao MASTER
somente após executar a consulta dentro da tabela derivada D
. Em OUTER APPLY
, ele usa a junção WHERE M.ID=D.ID
dentro do OUTER APPLY
, para que cada ID
em Master
será juntado com TOP 2
datas que trarão o seguinte resultado. 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. Quando precisamos de
LEFT JOIN
funcionalidade usando functions
. OUTER APPLY
pode ser usado como um substituto com LEFT JOIN
quando precisamos obter o resultado do Master
tabela e uma function
. SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
E a função vai aqui.
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE [email protected]
)
que gerou o seguinte resultado
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
3. Reter
NULL
valores ao não dinamizar Considere que você tem a tabela abaixo
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
Quando você usa
UNPIVOT
para trazer FROMDATE
E TODATE
para uma coluna, ele eliminará NULL
valores por padrão. SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
que gera o resultado abaixo. Observe que perdemos o registro de
Id
número 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
Nesses casos, um
APPLY
pode ser usado(tanto CROSS APPLY
ou OUTER APPLY
, que é intercambiável). SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
que forma o seguinte resultado e retém
Id
onde seu valor é 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 |
| 3 | NULL |
x------x-------------x