Quand dois-je utiliser Cross appliquer sur la jointure interne?

Quel est le but principal de l'utilisation de CROSS APPLY ?

j'ai lu (vaguement, par des messages sur Internet) que cross apply peut être plus efficace lors de la sélection sur de grands ensembles de données si vous êtes partitionnement. (La pagination vient à l'esprit)

je sais aussi que CROSS APPLY ne nécessite pas un UDF comme la table de droite.

dans la plupart des requêtes INNER JOIN (one-to-many relations), je pourrais les réécrire pour utiliser CROSS APPLY , mais ils me donnent toujours des plans d'exécution équivalents.

est-ce que quelqu'un peut me donner un bon exemple du moment où CROSS APPLY fait une différence dans les cas où INNER JOIN fonctionnera aussi?


modifier:

Voici un exemple trivial, où les plans d'exécution sont exactement les mêmes. (Montrez-m'en un où ils diffèrent et où cross apply est plus rapide/ plus efficace)

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
781
demandé sur Sarath Avanavu 2009-07-16 21:42:21

13 réponses

est-ce que quelqu'un peut me donner un bon exemple du moment où CROSS APPLY fait une différence dans les cas où INNER JOIN fonctionnera aussi bien?

voir l'article sur mon blog pour une comparaison détaillée des performances:

CROSS APPLY fonctionne mieux sur des choses qui n'ont pas simple "151970920 l'état".

celui-ci sélectionne 3 derniers enregistrements de t2 pour chaque enregistrement de 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

il ne peut pas être facilement formulé avec une condition INNER JOIN .

vous pourriez probablement faire quelque chose comme cela en utilisant CTE 's et la fonction de fenêtre:

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

, mais c'est moins lisible et probablement moins efficace.

mise à jour:

vient de vérifier.

master est un tableau d'environ 20,000,000 avec un PRIMARY KEY sur id .

Cette requête:

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

fonctionne pendant presque 30 secondes, tandis que celui-ci:

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

est instantané.

578
répondu Quassnoi 2013-09-17 21:30:15

cross apply vous permet parfois de faire des choses que vous ne pouvez pas faire avec inner join .

exemple (une erreur de syntaxe):

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

il s'agit d'une erreur de syntaxe , parce que, lorsqu'elle est utilisée avec inner join , les fonctions de table ne peuvent prendre variables ou constantes comme paramètres. (C'est-à-dire, le paramètre table function ne peut pas dépendre de la colonne d'une autre table.)

cependant:

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

c'est légal.

Edit: Ou alternativement, syntaxe plus courte: (by ErikE)

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

Edit:

Note: Informix 12.10 xC2+ a Lateral Derived Tables et Postgresql (9.3+) a Lateral Subqueries qui peut être utilisé à un effet similaire.

178
répondu nurettin 2016-06-14 18:01:00

considérez que vous avez deux tables.

MASTER TABLE

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

DETAILS TABLE

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

il y a de nombreuses situations où nous devons remplacer INNER JOIN par CROSS APPLY .

1. Joindre deux tableaux basés sur TOP n résultats

envisager si nous devons sélectionnez Id et Name de Master et les deux dernières dates pour chaque Id de 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

La requête ci-dessus génère le résultat suivant.

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

voir, il a généré des résultats pour les deux dernières dates avec les deux dernières Id et ensuite rejoint ces enregistrements seulement dans la requête externe sur Id , ce qui est faux. Pour ce faire, nous devons utiliser 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

et forme le résultat suivant.

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

Voici comment ça marche. La requête à l'intérieur de CROSS APPLY peut renvoyer à la table extérieure, où INNER JOIN ne peut pas faire cela (il lance compiler erreur.) Lorsque vous trouvez les deux dernières dates, l'assemblage se fait à l'intérieur de CROSS APPLY i.e., WHERE M.ID=D.ID .

2. Quand nous avons besoin de la fonctionnalité INNER JOIN en utilisant des fonctions.

CROSS APPLY peut être utilisé comme un remplacement avec INNER JOIN quand nous avons besoin d'obtenir le résultat de Master table et un function .

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

et voici la fonction

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

qui a généré le résultat suivant

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

AVANTAGE ADDITIONNEL DE CROSS APPLIQUER

APPLY peut être utilisé en remplacement de UNPIVOT . Soit CROSS APPLY ou OUTER APPLY peuvent être utilisés ici, qui sont interchangeables.

considérer vous avez le tableau ci-dessous(nommé 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

La requête est ci-dessous.

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

qui vous apporte le résultat

  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
répondu Sarath Avanavu 2016-08-31 19:25:21

voici un exemple où L'application croisée fait une énorme différence avec la performance:

utiliser CROSS appliquer pour optimiser les jointures entre les conditions

notez qu'en plus de remplacer inner joins vous pouvez réutiliser le code tel que les dates tronquées sans payer la pénalité de performance pour l'implication UDFs scalaires, par exemple: calcul du troisième mercredi du mois avec UDFs inline

37
répondu A-K 2009-07-16 19:48:53

il me semble que CROSS APPLY peut combler une certaine lacune en travaillant avec des champs calculés dans des requêtes complexes/imbriquées, et les rendre plus simples et plus lisibles.

exemple Simple: vous avez une date de naissance et vous voulez présenter plusieurs champs liés à l'âge qui s'appuieront également sur d'autres sources de données (comme l'emploi), comme L'âge, le groupe D'âge, L'âge fatigant, Minimumretiredate, etc. pour une utilisation dans votre application utilisateur (Excel PivotTables, par exemple).

les Options sont limitées et rarement élégantes:

  • " JOIN subqueries ne peut pas introduire de nouvelles valeurs dans l'ensemble de données basé sur des données dans la requête parent (il doit se tenir sur son propre).

  • les UDF sont nets, mais lents, car ils ont tendance à empêcher les opérations parallèles. Et étant une entité distincte peut être une bonne (moins de code) ou mauvais (où est le code).

  • La jonction des tables. Parfois, ça peut marcher, mais bientôt, vous rejoignez des sous-séries avec des tonnes de syndicats. Gros gâchis.

  • Créez encore une autre vue à usage unique, en supposant que vos calculs ne nécessitent pas de données obtenues à mi-chemin dans votre requête principale.

  • tableaux intermédiaires. Oui... cela fonctionne généralement, et souvent une bonne option, car ils peuvent être indexés et rapides, mais les performances peuvent également baisser en raison de de Les déclarations de mise à jour ne sont pas parallèles et ne permettent pas de formules en cascade (résultats de réutilisation) pour mettre à jour plusieurs champs dans la même déclaration. Et parfois tu préférerais faire les choses en un seul passage.

  • Imbrication des requêtes. Oui, à tout moment, vous pouvez mettre la parenthèse sur l'ensemble de votre requête et l'utiliser comme une sous-requête sur laquelle vous pouvez manipuler les données source et les champs calculés de la même façon. Mais tu ne peux pas faire ça avant que ça devienne moche. Très laid.

  • code de répétition. Quelle est la plus grande valeur de 3 long (CASE...AUTRE...FIN) états? Ça va être lisible!

    • dites à vos clients de calculer les maudites choses eux-mêmes.

ai-je manqué quelque chose? Probablement, donc n'hésitez pas à commenter. Mais hé, CROSS APPLY est comme une aubaine dans de telles situations: il suffit d'ajouter un simple CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl et voilà! Votre nouveau champ est maintenant prêt à être utilisé pratiquement comme il l'a toujours été dans vos données source.

Les valeurs

introduites par CROSS peuvent s'appliquer...

  • doit être utilisé pour créer un ou plusieurs champs calculés sans ajouter de problèmes de performance, de complexité ou de lisibilité au mélange
  • comme avec les jointures, plusieurs déclarations D'application croisée subséquentes peuvent se référer à elles-mêmes: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • vous pouvez utiliser les valeurs introduites par une croix S'appliquent dans les conditions D'adhésion suivantes
  • en prime, il y a l'aspect de la fonction de valeur de Table

Dang, il n'y a rien qu'ils ne puissent faire!

33
répondu mtone 2016-08-24 23:59:04

Cross apply fonctionne aussi bien avec un champ XML. Si vous souhaitez sélectionner des valeurs de nœud en combinaison avec d'autres champs.

par exemple, si vous avez une table contenant du xml

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

à l'Aide de la requête

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

retournera un résultat

xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1     test1            1                    Benefits
1     test1            4                    FINRPTCOMPANY
12
répondu Chris 2013-02-01 18:52:17

je suppose que cela doit être la lisibilité ;)

croix appliquer sera un peu unique pour les personnes lisant pour leur dire qu'un UDF est utilisé qui sera appliqué à chaque ligne de la table à gauche.

bien sûr, il ya d'autres limites où une croix S'applique est mieux utilisé que de rejoindre que d'autres amis ont posté ci-dessus.

5
répondu shahkalpesh 2009-07-16 18:12:28

Cross apply peut être utilisé pour remplacer les sous-commandes où vous avez besoin d'une colonne de la sous-commande

sous-requête

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

ici Je ne serai pas en mesure de sélectionner les colonnes de la table d'entreprise ainsi, en utilisant cross appliquer

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
répondu balaji dileep kumar 2014-12-11 09:51:12

voici un article qui explique tout, avec leur différence de performance et d'utilisation sur les jointures.

SQL Server CROSS APPLY and OUTER APPLY over JOINS

comme le suggère cet article, il n'y a pas de différence de performance entre les deux pour les opérations normales de jointure (intérieure et croisée).

enter image description here

la différence d'usage arrive quand vous devez faire une requête comme celle-ci:

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)

C'est-à-dire quand vous devez vous rapporter à la fonction. Cela ne peut pas être fait en utilisant la jointure intérieure, qui vous donnerait l'erreur "l'identificateur multi-parties "D. DepartmentID" ne pouvait pas être lié." ici la valeur est passée à la fonction comme chaque ligne est lue. Sons cool pour moi. :)

3
répondu Shanid 2016-03-21 06:03:16

Eh bien, je ne suis pas sûr si cela peut être considéré comme une raison d'utiliser Cross Apply versus Inner Join, mais cette requête a été répondu pour moi dans un Forum post en utilisant Cross Apply, donc je ne suis pas sûr s'il y a une méthode equalivent en utilisant 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

2
répondu user1054326 2012-03-08 19:51:43

C'est peut-être une vieille question, mais j'aime toujours la puissance de CROSS APPLY pour simplifier la réutilisation de la logique et de fournir un mécanisme de "chaînage" pour les résultats.

j'ai fourni un Fiddle SQL ci-dessous qui montre un exemple simple de la façon dont vous pouvez utiliser CROSS APPLY pour effectuer des opérations logiques complexes sur votre ensemble de données sans que les choses se gâtent. Il n'est pas difficile d'extrapoler à partir d'ici, des calculs plus complexes.

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

0
répondu mrmillsy 2015-10-06 01:41:57

l'essence de L'opérateur D'application est de permettre la corrélation entre le côté gauche et le côté droit de l'opérateur dans la clause de.

contrairement à la jointure, la corrélation entre les entrées n'est pas permise.

en parlant de corrélation dans L'application opérateur, je veux dire sur le côté droit nous pouvons mettre:

  • une table dérivée - comme une sous-requête en corrélation avec un alias
  • une fonction à valeur de table - un vue conceptuelle avec paramètres, où le paramètre peut se référer au côté gauche

les deux peuvent retourner plusieurs colonnes et lignes.

0
répondu Raf 2018-04-29 19:36:28

cela a déjà été très bien répondu techniquement, mais permettez-moi de donner un exemple concret de la façon dont il est extrêmement utile:

disons que vous avez deux tables, client et commander. Les clients ont de nombreuses Commandes.

je veux créer une vue qui me donne des détails sur les clients, et la plus récente commande qu'ils ont faite. Avec des jointures, cela nécessiterait quelques jointures et agrégation qui n'est pas jolie. Mais avec Cross appliquer, son super facile:

SELECT *
FROM Customer
CROSS APPLY (
  SELECT TOP 1 *
  FROM Order
  WHERE Order.CustomerId = Customer.CustomerId
  ORDER BY OrderDate DESC
) T
0
répondu Apneal 2018-07-09 19:32:33