Kiedy należy stosować Cross Apply na Złącze wewnętrzne?

Jaki jest główny cel stosowania CROSS APPLY?

Przeczytałem (niejasno, przez posty w Internecie), że cross apply może być bardziej wydajny przy wyborze dużych zbiorów danych, jeśli partycjonujesz. (Przywoływanie przychodzi mi na myśl)

Wiem też, że CROSS APPLY nie wymaga UDF jako prawej tabeli.

W większości INNER JOIN zapytań (relacje jeden do wielu), mógłbym je przepisać na CROSS APPLY, ale zawsze dają mi równoważne plany wykonania.

Czy ktoś może mi podać dobry przykład Kiedy CROSS APPLY robi różnicę w tych przypadkach gdzie INNER JOIN również zadziała?


Edit:

Oto trywialny przykład, gdzie plany egzekucji są dokładnie takie same. (Pokaż mi jeden, gdzie się różnią i gdzie cross apply jest szybszy/bardziej wydajny)

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
Author: Sarath Avanavu, 2009-07-16

13 answers

Czy ktoś może dać mi dobry przykład, kiedy CROSS APPLY robi różnicę w tych przypadkach, w których wewnętrzne JOIN będzie działać, jak również?

Zobacz artykuł na moim blogu, aby uzyskać szczegółowe porównanie wydajności:

CROSS APPLY działa lepiej na rzeczach, które nie mają prostego JOIN warunku.

Ten wybiera 3 ostatnie rekordy z t2 dla każdego rekordu z 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

Nie może być łatwo sformułowany z INNER JOIN warunkiem.

Prawdopodobnie możesz zrobić coś takiego używając CTE ' S i funkcji window:

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

, ale to jest mniej czytelne i prawdopodobnie mniej wydajne.

Update:

Właśnie sprawdziłem.

master jest tabelą około 20,000,000 rekordów z PRIMARY KEY na id.

To zapytanie:

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

Trwa prawie 30} sekund, podczas gdy ten:

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

Jest natychmiastowy.

 578
Author: Quassnoi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-17 21:30:15

cross apply czasami pozwala Ci robić rzeczy, których nie możesz zrobić z inner join.

Przykład (błąd składni):

select F.* from sys.objects O  
inner join dbo.myTableFun(O.name) F   
on F.schema_id= O.schema_id

Jest to błąd składni , ponieważ, gdy jest używany z inner join, funkcje tabeli mogą przyjmować tylko zmienne lub stałe jako parametry. (Tzn. parametr funkcji tabeli nie może zależeć od Kolumny innej tabeli.)

Jednakże:

select F.* from sys.objects O  
cross apply ( select * from dbo.myTableFun(O.name) ) F  
where F.schema_id= O.schema_id
To jest legalne.

Edit: Lub alternatywnie krótsza składnia: (przez ErikE)

select F.* from sys.objects O  
cross apply dbo.myTableFun(O.name) F
where F.schema_id= O.schema_id

Edit:

Uwaga: Informix 12.10 xC2 + ma boczne pochodne tabele , a Postgresql (9.3+) ma boczne zapytania subqueries , które mogą być użyte do podobnego efektu.

 178
Author: nurettin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 18:01:00

Weź pod uwagę, że masz dwa stoliki.

TABELA GŁÓWNA

x------x--------------------x
| Id   |        Name        |
x------x--------------------x
|  1   |          A         |
|  2   |          B         |
|  3   |          C         |
x------x--------------------x

SZCZEGÓŁY TABELA

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

Jest wiele sytuacji, w których musimy zastąpić INNER JOIN CROSS APPLY.

1. Połącz dwie tabele na podstawie wyników TOP n

Zastanów się, czy musimy wybrać Id i Name z Master i dwie ostatnie daty dla każdej Id z 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

Powyższe zapytanie generuje następujący wynik.

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

Zobacz, wygenerował wyniki dla dwóch ostatnich dat z dwiema ostatnimi datami Id, a następnie dołączył te rekordy tylko w zewnętrznym zapytaniu na Id, co jest złe. Aby to osiągnąć, musimy użyć 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

I tworzy następujący wynik.

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

Oto jak to działa. Zapytanie wewnątrz CROSS APPLY może odwoływać się do zewnętrznej tabeli, gdzie {[12] } nie może tego zrobić (to wyrzuca błąd kompilacji). Po znalezieniu dwóch ostatnich dat, łączenie odbywa się wewnątrz CROSS APPLY tj. WHERE M.ID=D.ID.

2. Kiedy potrzebujemy INNER JOIN funkcjonalności za pomocą funkcji.

CROSS APPLY może być używany jako zamiennik z INNER JOIN, gdy musimy uzyskać wynik z Master tabeli i function.

SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
CROSS APPLY dbo.FnGetQty(M.ID) C

A oto funkcja

CREATE FUNCTION FnGetQty 
(   
    @Id INT 
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT ID,PERIOD,QTY 
    FROM DETAILS
    WHERE ID=@Id
)

Który wygenerował następujące wynik

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

DODATKOWA ZALETA ZASTOSOWANIA KRZYŻOWEGO

APPLY może być stosowany jako zamiennik UNPIVOT. Można tu stosować zarówno CROSS APPLY, jak i OUTER APPLY, które są wymienne.

Rozważ, że masz poniższą tabelę (o nazwie 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

Zapytanie znajduje się poniżej.

SELECT DISTINCT ID,DATES
FROM MYTABLE 
CROSS APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)

Co daje wynik

  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
 116
Author: Sarath Avanavu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-31 19:25:21

Oto przykład, kiedy CROSS APPLY ma ogromne znaczenie dla wydajności:

Użycie CROSS APPLY do optymalizacji połączeń pomiędzy Warunkami

Zauważ, że oprócz zastąpienia łączników wewnętrznych możesz również ponownie użyć kodu, takiego jak obcinanie dat bez płacenia kary za zaangażowanie skalarnego UDFs, na przykład: Obliczanie trzeciej środy miesiąca za pomocą wbudowanego UDFs

 37
Author: A-K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-16 19:48:53

Wydaje mi się, że CROSS APPLY może wypełnić pewną lukę podczas pracy z polami obliczeniowymi w złożonych/zagnieżdżonych zapytaniach i uczynić je prostszymi i bardziej czytelnymi.

Prosty przykład: masz DoB i chcesz przedstawić wiele pól związanych z wiekiem, które będą również opierać się na innych źródłach danych (takich jak zatrudnienie), takich jak Age, AgeGroup, AgeAtHiring, MinimumRetirementDate itp. do użytku w aplikacji użytkownika końcowego (na przykład w tabelach przestawnych Excel).

Opcje są ograniczone i rzadko "elegant": {]}

  • Zapytania podrzędne JOIN nie mogą wprowadzać nowych wartości do zbioru danych na podstawie danych w zapytaniu nadrzędnym (musi być samodzielne).

  • UDF są schludne, ale powolne, ponieważ zapobiegają operacjom równoległym. A bycie odrębnym podmiotem może być czymś dobrym (mniej kodu) lub złym(gdzie jest kod).

  • Stoły połączeniowe. Czasami mogą zadziałać, ale wkrótce dołączysz do subqueries z mnóstwem związków. Wielki bałagan.

  • Utwórz kolejny widok jednofunkcyjny, zakładając, że obliczenia nie wymagają danych uzyskanych w trakcie głównego zapytania.

  • Tablice pośredniczące. Tak... to zwykle działa i często jest dobrą opcją, ponieważ mogą być indeksowane i szybkie, ale wydajność może również spadać z powodu aktualizacji instrukcji nie są równoległe i nie pozwalają na kaskadowe formuły (ponowne wykorzystanie wyników), aby zaktualizować kilka pól w ramach tej samej instrukcji. I czasami po prostu wolisz robić rzeczy w jednym pasuję.

  • Zagnieżdżanie zapytań. Tak w dowolnym momencie możesz umieścić nawias na całym zapytaniu i użyć go jako zapytania podrzędnego, na którym możesz manipulować danymi źródłowymi i polami obliczeniowymi. Ale możesz to robić tylko tyle, zanim zrobi się brzydko. Bardzo brzydka.

  • Powtarzający się kod. Jaka jest największa wartość 3 long (CASE...ELSE...Koniec) wypowiedzi? To będzie czytelne!

    • Powiedz swoim Klientom, aby obliczyli cholerne rzeczy siebie.
Coś mnie ominęło? Prawdopodobnie, więc zapraszam do komentowania. Ale hej, CROSS APPLY jest jak dar niebios w takich sytuacjach: wystarczy dodać prosty CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl i voila! Twoje nowe pole jest teraz gotowe do użycia praktycznie tak, jak zawsze było w Twoich danych źródłowych.

Wartości wprowadzone przez CROSS APPLY can...

  • być używane do tworzenia jednego lub wielu pól obliczeniowych bez dodawania problemów z wydajnością, złożonością lub czytelnością mix
  • podobnie jak w przypadku JOINs, kilka kolejnych wyrażeń CROSS APPLY może odnosić się do siebie: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • Można używać wartości wprowadzonych przez krzyżyk APPLY w kolejnych warunkach JOIN
  • jako bonus, jest aspekt funkcji wartej Tabeli
Cholera, nic nie mogą zrobić!
 33
Author: mtone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-24 23:59:04

Cross apply działa również z polem XML. Jeśli chcesz wybrać wartości węzłów w połączeniu z innymi polami.

Na przykład, jeśli masz tabelę zawierającą jakiś xml

<root>
    <subnode1>
       <some_node value="1" />
       <some_node value="2" />
       <some_node value="3" />
       <some_node value="4" />
    </subnode1>
</root>

Korzystanie z zapytania

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

Zwróci wynik

xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1     test1            1                    Benefits
1     test1            4                    FINRPTCOMPANY
 12
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 18:52:17

Chyba powinna być czytelność;)

CROSS APPLY będzie nieco unikalny dla osób czytających, aby powiedzieć im, że używany jest UDF, który zostanie zastosowany do każdego wiersza z tabeli po lewej stronie.

Oczywiście, istnieją inne ograniczenia, w których krzyż APPLY jest lepiej używany niż dołączyć, które inni znajomi opublikowali powyżej.

 5
Author: shahkalpesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-16 18:12:28

Cross apply może być użyty do zastąpienia subquery tam, gdzie potrzebna jest kolumna subquery

Subquery

select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')

Tutaj Nie będę mógł wybrać kolumn tabeli firmy tak więc, używając 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
 4
Author: balaji dileep kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-11 09:51:12

Oto artykuł, który wyjaśnia to wszystko, z ich różnicą wydajności i użycia nad połączeniami.

SQL Server CROSS APPLY and OUTER APPLY over JOINS

Jak sugerowano w tym artykule, nie ma różnicy wydajności między nimi dla normalnych operacji łączenia (wewnętrzne i krzyżowe).

Tutaj wpisz opis obrazka

Różnica użycia pojawia się, gdy musisz wykonać takie zapytanie:

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)

To znaczy, kiedy musisz odnosić się do funkcji. To nie może być wykonane za pomocą połączenia wewnętrznego, co daje błąd "wieloczęściowy identyfikator " D. DepartmentID" nie może być związany." tutaj wartość jest przekazywana do funkcji podczas odczytu każdego wiersza. Brzmi fajnie. :)

 3
Author: Shanid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-21 06:03:16

Cóż, nie jestem pewien, czy kwalifikuje się to jako powód, aby użyć Cross Apply versus Inner Join, ale to pytanie zostało udzielone mi w poście na Forum za pomocą Cross Apply, więc nie jestem pewien, czy istnieje equalivent metoda za pomocą Inner Join:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

-- Declare the Topical Neighborhood
@TopicalNeighborhood nchar(255)

Jako 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

 2
Author: user1054326,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-08 19:51:43

To może Stare pytanie, ale nadal kocham moc Cross APPLY, aby uprościć ponowne użycie logiki i zapewnić mechanizm "łańcuchowania" wyników.

Podałem Skrzypek SQL poniżej, który pokazuje prosty przykład, jak można użyć CROSS APPLY do wykonywania złożonych operacji logicznych na zestawie danych bez rzeczy getting w ogóle bałagan. Nietrudno ekstrapolować stąd bardziej złożone obliczenia.

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

 0
Author: mrmillsy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-06 01:41:57

Istotą operatora APPLY jest umożliwienie korelacji pomiędzy lewą i prawą stroną operatora w klauzuli FROM.

W przeciwieństwie do JOIN, korelacja między wejściami nie jest dozwolona.

Mówiąc o korelacji w operatorze APPLY, to znaczy po prawej stronie możemy umieścić:

  • pochodna tabela - jako skorelowane podquery z aliasem
  • funkcja o wartości tabelarycznej - koncepcyjny widok z parametrami, w którym parametr może odnosić się do lewej Strona

Oba mogą zwracać wiele kolumn i wierszy.

 0
Author: Raf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-29 19:36:28

ODPOWIEDŹ na to pytanie została już udzielona bardzo dobrze technicznie, ale podam konkretny przykład, jak bardzo jest to przydatne:

Powiedzmy, że masz dwa stoły, klienta i zamówienia. Klienci mają wiele zamówień.

Chcę stworzyć widok, który pokaże mi szczegóły o klientach i ostatnim zamówieniu, które złożyli. W przypadku just JOINS wymagałoby to pewnych samo-złączeń i agregacji, co nie jest ładne. Ale z Cross Apply, to bardzo proste:

SELECT *
FROM Customer
CROSS APPLY (
  SELECT TOP 1 *
  FROM Order
  WHERE Order.CustomerId = Customer.CustomerId
  ORDER BY OrderDate DESC
) T
 0
Author: Apneal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-09 19:32:33