Когда я должен использовать перекрестное применение поверх внутреннего соединения?
-
16-09-2019 - |
Вопрос
Какова основная цель использования ПЕРЕКРЕСТНОЕ НАНЕСЕНИЕ?
Я читал (смутно, через сообщения в Интернете), что cross apply
может быть более эффективным при выборе по большим наборам данных, если вы выполняете разбиение на разделы.(На ум приходит подкачка по страницам)
Я также знаю , что CROSS APPLY
не требует UDF в качестве правой таблицы.
В большинстве INNER JOIN
запросы (отношения "один ко многим"), я мог бы переписать их, чтобы использовать CROSS APPLY
, но они всегда дают мне эквивалентные планы выполнения.
Кто-нибудь может привести мне хороший пример того, когда CROSS APPLY
имеет значение в тех случаях, когда INNER JOIN
будет ли работать так же хорошо?
Редактировать:
Вот тривиальный пример, где планы выполнения точно такие же.(Покажите мне хоть одно, чем они отличаются и где cross apply
быстрее / эффективнее)
create table Company (
companyId int identity(1,1)
, companyName varchar(100)
, zipcode varchar(10)
, constraint PK_Company primary key (companyId)
)
GO
create table Person (
personId int identity(1,1)
, personName varchar(100)
, companyId int
, constraint FK_Person_CompanyId foreign key (companyId) references dbo.Company(companyId)
, constraint PK_Person primary key (personId)
)
GO
insert Company
select 'ABC Company', '19808' union
select 'XYZ Company', '08534' union
select '123 Company', '10016'
insert Person
select 'Alan', 1 union
select 'Bobby', 1 union
select 'Chris', 1 union
select 'Xavier', 2 union
select 'Yoshi', 2 union
select 'Zambrano', 2 union
select 'Player 1', 3 union
select 'Player 2', 3 union
select 'Player 3', 3
/* using CROSS APPLY */
select *
from Person p
cross apply (
select *
from Company c
where p.companyid = c.companyId
) Czip
/* the equivalent query using INNER JOIN */
select *
from Person p
inner join Company c on p.companyid = c.companyId
Решение
Может ли кто -нибудь привести мне хороший пример того, когда применяется Cross, имеет значение в тех случаях, где внутреннее соединение также будет работать?
Смотрите статью в моем блоге для подробного сравнения производительности:
CROSS APPLY
работает лучше на вещах, которые не имеют простых JOIN
условие.
Этот выбирает 3
Последние записи от t2
Для каждой записи из t1
:
SELECT t1.*, t2o.*
FROM t1
CROSS APPLY
(
SELECT TOP 3 *
FROM t2
WHERE t2.t1_id = t1.id
ORDER BY
t2.rank DESC
) t2o
Его нельзя легко сформулировать с помощью INNER JOIN
условие.
Вы могли бы сделать что -то подобное, используя CTE
S и Window Function:
WITH t2o AS
(
SELECT t2.*, ROW_NUMBER() OVER (PARTITION BY t1_id ORDER BY rank) AS rn
FROM t2
)
SELECT t1.*, t2o.*
FROM t1
INNER JOIN
t2o
ON t2o.t1_id = t1.id
AND t2o.rn <= 3
, но это менее читаемо и, вероятно, менее эффективно.
Обновлять:
Только что проверил.
master
это таблица примерно 20,000,000
записи с PRIMARY KEY
на id
.
Этот запрос:
WITH q AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn
FROM master
),
t AS
(
SELECT 1 AS id
UNION ALL
SELECT 2
)
SELECT *
FROM t
JOIN q
ON q.rn <= t.id
Бежит почти 30
секунды, пока это:
WITH t AS
(
SELECT 1 AS id
UNION ALL
SELECT 2
)
SELECT *
FROM t
CROSS APPLY
(
SELECT TOP (t.id) m.*
FROM master m
ORDER BY
id
) q
это мгновенно.
Другие советы
cross apply
иногда позволяет вам делать то, что вы не можете сделать inner join
.
Пример (синтаксическая ошибка):
select F.* from sys.objects O
inner join dbo.myTableFun(O.name) F
on F.schema_id= O.schema_id
Это ошибка синтаксиса, потому что при использовании с inner join
, Функции таблицы могут занять только переменные или константы как параметры. (То есть, параметр функции таблицы не может зависеть от столбца другой таблицы.)
Однако:
select F.* from sys.objects O
cross apply ( select * from dbo.myTableFun(O.name) ) F
where F.schema_id= O.schema_id
Это законно.
Редактировать:Или альтернативно, более короткий синтаксис: (по эрике)
select F.* from sys.objects O
cross apply dbo.myTableFun(O.name) F
where F.schema_id= O.schema_id
Редактировать:
Примечание: Informix 12.10 XC2+ имеет Боковые полученные таблицы и PostgreSQL (9,3+) Боковые подножки который может быть использован для аналогичного эффекта.
Считайте, что у вас есть две таблицы.
Мастер -таблица
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. Присоединяйтесь к двум таблицам на основе TOP n
полученные результаты
Подумайте, если нам нужно выбрать 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
Дополнительное преимущество креста применяется
APPLY
можно использовать в качестве замены для UNPIVOT
. Анкет Либо CROSS APPLY
или же OUTER APPLY
можно использовать здесь, которые взаимозаменяемы.
Считайте, что у вас есть таблица ниже (названная MYTABLE
).
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
Запрос ниже.
SELECT DISTINCT ID,DATES
FROM MYTABLE
CROSS APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
Что приносит вам результат
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
Мне кажется, что применение Креста может заполнить определенный разрыв при работе с расчетными полями в сложных/вложенных запросах и сделать их более простыми и читаемыми.
Простой пример: у вас есть DOB, и вы хотите представить несколько возрастных полей, которые также будут полагаться на другие источники данных (такие как занятость), такие как возраст, возрастная группа, Ageathinging, Minimumrementirementdate и т. Д. Для использования в вашем приложении конечного пользователя (Например, Excel Pivottables).
Варианты ограничены и редко элегантны:
Присоединение подразделений не может ввести новые значения в наборе данных на основе данных в родительском запросе (он должен стоять самостоятельно).
UDF являются аккуратными, но медленными, поскольку они склонны предотвратить параллельные операции. И быть отдельной сущностью может быть хорошим (меньшим кодом) или плохой (где код).
Соединенные таблицы. Иногда они могут работать, но достаточно скоро вы присоединяетесь к подразделениям с тоннами профсоюзов. Большой беспорядок.
Создайте еще один одноцелевой вид, предполагая, что ваши расчеты не требуют данных, полученных в середине вашего основного запроса.
Посреднические таблицы. Да ... это обычно работает, и часто хороший вариант, поскольку их можно индексировать и быстро, но производительность также может снизиться из -за того, что операторы обновления не являются параллельными и не позволяя каскадным формулам (результаты повторного использования) обновлять несколько полей в пределах то же самое утверждение. И иногда вы просто предпочитаете делать что -то в одном проходе.
Запросы гнездования. Да в любой момент вы можете поставить скобку на весь свой запрос и использовать его в качестве подзадна, на котором вы можете манипулировать исходными данными и рассчитанными полями. Но вы можете сделать это так много, прежде чем это станет уродливым. Очень страшный.
Повторяющий код. Какое наибольшее значение 3 длинных (случай ... иначе ... конец) операторов? Это будет читаемо!
- Скажите своим клиентам, чтобы они сами рассчитали проклятые вещи.
Я что-то пропустил? Возможно, так не стесняйтесь комментировать. Но, эй, применить крест - это как находка в таких ситуациях: вы просто добавляете простой CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl
И вуаля! Ваше новое поле теперь готово для использования практически, как всегда было в ваших исходных данных.
Значения, введенные через Cross Apply Can ...
- использоваться для создания одного или нескольких рассчитанных полей без добавления производительности, сложности или читабельности к миксу.
- Как и в случае с объединением, несколько последующих заявлений применения могут ссылаться на себя:
CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
- Вы можете использовать значения, введенные применимыми применимыми в последующих условиях соединения
- В качестве бонуса есть аспект функции, а также на таблице
Черт, они ничего не могут сделать!
Cross Apply хорошо работает с полем XML. Если вы хотите выбрать значения узла в сочетании с другими полями.
Например, если у вас есть таблица, содержащая XML
<root> <subnode1> <some_node value="1" /> <some_node value="2" /> <some_node value="3" /> <some_node value="4" /> </subnode1> </root>
Используя запрос
SELECT
id as [xt_id]
,xmlfield.value('(/root/@attribute)[1]', 'varchar(50)') root_attribute_value
,node_attribute_value = [some_node].value('@value', 'int')
,lt.lt_name
FROM dbo.table_with_xml xt
CROSS APPLY xmlfield.nodes('/root/subnode1/some_node') as g ([some_node])
LEFT OUTER JOIN dbo.lookup_table lt
ON [some_node].value('@value', 'int') = lt.lt_id
Вернет результат
xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1 test1 1 Benefits
1 test1 4 FINRPTCOMPANY
Технически на этот вопрос уже был дан очень хороший ответ, но позвольте мне привести конкретный пример того, насколько это чрезвычайно полезно:
Допустим, у вас есть две таблицы: Customer и Order.У клиентов много Заказов.
Я хочу создать представление, которое предоставляет мне подробную информацию о клиентах и самый последний заказ, который они сделали.При использовании just JOINS для этого потребовались бы некоторые самосоединения и агрегирование, что не очень красиво.Но с перекрестным нанесением это очень просто:
SELECT *
FROM Customer
CROSS APPLY (
SELECT TOP 1 *
FROM Order
WHERE Order.CustomerId = Customer.CustomerId
ORDER BY OrderDate DESC
) T
Применить крест можно использовать для замены подразделений, где вам нужен столбец подзадна
подпрограмма
select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')
Здесь я не смогу выбрать столбцы таблицы компании, поэтому применить Cross Apply
select P.*,T.CompanyName
from Person p
cross apply (
select *
from Company C
where p.companyid = c.companyId and c.CompanyName like '%yyy%'
) T
Я думаю, это должно быть читаемости;)
Применение Cross будет несколько уникальным для людей, читающих им, чтобы сказать им, что используется UDF, который будет применяться к каждой строке со стола слева.
Конечно, есть и другие ограничения, когда применяется применение креста лучше, чем присоединение, которые другие друзья опубликовали выше.
Here is an article that explains it all, with their performance difference and usage over JOINS.
SQL Server CROSS APPLY and OUTER APPLY over JOINS
As suggested in this article, there is no performance difference between them for normal join operations (INNER AND CROSS).
The usage difference arrives when you have to do a query like this:
CREATE FUNCTION dbo.fn_GetAllEmployeeOfADepartment(@DeptID AS INT)
RETURNS TABLE
AS
RETURN
(
SELECT * FROM Employee E
WHERE E.DepartmentID = @DeptID
)
GO
SELECT * FROM Department D
CROSS APPLY dbo.fn_GetAllEmployeeOfADepartment(D.DepartmentID)
That is, when you have to relate with function. This cannot be done using INNER JOIN, which would give you the error "The multi-part identifier "D.DepartmentID" could not be bound." Here the value is passed to the function as each row is read. Sounds cool to me. :)
Well I am not sure if this qualifies as a reason to use Cross Apply versus Inner Join, but this query was answered for me in a Forum Post using Cross Apply, so I am not sure if there is an equalivent method using Inner Join:
Create PROCEDURE [dbo].[Message_FindHighestMatches]
-- Declare the Topical Neighborhood
@TopicalNeighborhood nchar(255)
AS BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
Create table #temp
(
MessageID int,
Subjects nchar(255),
SubjectsCount int
)
Insert into #temp Select MessageID, Subjects, SubjectsCount From Message
Select Top 20 MessageID, Subjects, SubjectsCount,
(t.cnt * 100)/t3.inputvalues as MatchPercentage
From #temp
cross apply (select count(*) as cnt from dbo.Split(Subjects,',') as t1
join dbo.Split(@TopicalNeighborhood,',') as t2
on t1.value = t2.value) as t
cross apply (select count(*) as inputValues from dbo.Split(@TopicalNeighborhood,',')) as t3
Order By MatchPercentage desc
drop table #temp
END
The essence of the APPLY operator is to allow correlation between left and right side of the operator in the FROM clause.
In contrast to JOIN, the correlation between inputs is not allowed.
Speaking about correlation in APPLY operator, I mean on the right hand side we can put:
- a derived table - as a correlated subquery with an alias
- a table valued function - a conceptual view with parameters, where the parameter can refer to the left side
Both can return multiple columns and rows.
This is perhaps an old question, but I still love the power of CROSS APPLY to simplify the re-use of logic and to provide a "chaining" mechanism for results.
I've provided a SQL Fiddle below which shows a simple example of how you can use CROSS APPLY to perform complex logical operations on your data set without things getting at all messy. It's not hard to extrapolate from here more complex calculations.