En LEFT JOIN
bör ersättas med OUTER APPLY
i följande situationer.
1. Om vi vill sammanfoga två tabeller baserade på TOP n
resultat
Fundera på om vi behöver välja Id
och Name
från Master
och de två senaste datumen för varje Id
från Details
bord.
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
vilket ger följande resultat
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
Detta kommer att ge felaktiga resultat, dvs. det kommer endast att ta med de senaste två datumdata från Details
tabell oavsett Id
även om vi går med med Id
. Så den rätta lösningen är att använda 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
Så här fungerar :I LEFT JOIN
, TOP 2
datum kommer att kopplas till MASTER
endast efter att ha kört frågan i den härledda tabellen D
. I OUTER APPLY
, använder den anslutande WHERE M.ID=D.ID
inuti OUTER APPLY
, så att varje ID
i Master
kommer att förenas med TOP 2
datum som ger följande resultat.
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. När vi behöver LEFT JOIN
funktionalitet med functions
.
OUTER APPLY
kan användas som ersättning med LEFT JOIN
när vi behöver få resultat från Master
tabell och en function
.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
Och funktionen går här.
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE [email protected]
)
vilket genererade följande resultat
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. Behåll NULL
värden vid unpivotering
Tänk på att du har tabellen nedan
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
När du använder UNPIVOT
för att hämta FROMDATE
OCH TODATE
till en kolumn kommer den att eliminera NULL
värden som standard.
SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
vilket genererar resultatet nedan. Observera att vi har missat posten för Id
nummer 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
I sådana fall en APPLY
kan användas (antingen CROSS APPLY
eller OUTER APPLY
, som är utbytbar).
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
som bildar följande resultat och behåller Id
där dess värde är 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