Существуют различные ситуации, в которых нельзя избежать 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
для переноса FROMDATE
AND 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