Існують різні ситуації , в яких ви не можете уникнути 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