В различных ситуациях вы не можете избежать CROSS APPLY
или OUTER APPLY
.
Предположим, у вас две таблицы.
MASTER TABLE
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
и 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
и 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
. В таких случаях 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