Існують різні ситуації , в яких ви не можете уникнути 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
КРЕСНА ЗАЯВА
Є багато ситуацій , коли необхідно замінити INNER JOIN
з CROSS APPLY
.
1. Якщо ми хочемо об'єднати 2 таблиці за TOP n
результатами з INNER JOIN
функціональністю
Поміркуйте, чи потрібно нам вибрати Id
і Name
від, Master
і останні дві дати для кожної Id
з Details table
.
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
функціональність за допомогою функцій.
CROSS APPLY
може використовуватися як заміна, INNER JOIN
коли нам потрібно отримати результат з Master
таблиці та a function
.
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. Якщо ми хочемо об'єднати 2 таблиці за TOP n
результатами з LEFT JOIN
функціональністю
Поміркуйте, чи потрібно нам вибрати ідентифікатор та ім’я з Master
та останні два дати для кожного Id із Details
таблиці.
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. Коли нам потрібна LEFT JOIN
функціональність використання functions
.
OUTER APPLY
може використовуватися як заміна, LEFT JOIN
коли нам потрібно отримати результат з Master
таблиці та a function
.
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
І 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
У таких випадках a CROSS APPLY
або OUTER APPLY
буде корисно
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
який утворює наступний результат і зберігає 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 |
| 3 | NULL |
x------x-------------x