Когда я должен использовать перекрестное применение поверх внутреннего соединения?

StackOverflow https://stackoverflow.com/questions/1139160

Вопрос

Какова основная цель использования ПЕРЕКРЕСТНОЕ НАНЕСЕНИЕ?

Я читал (смутно, через сообщения в Интернете), что 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 условие.

Вы могли бы сделать что -то подобное, используя CTES и 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).

enter image description here

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.

http://sqlfiddle.com/#!3/23862/2

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top