Существуют различные ситуации, в которых нельзя избежать 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функциональность с использованием functions.
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функциональностью
Подумайте, нужно ли нам выбирать Id и Name из 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для переноса FROMDATEAND 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)
который формирует следующий результат и сохраняет 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