web-dev-qa-db-fra.com

Quand devrais-je utiliser l'application croisée sur la jointure interne?

Quel est le but principal d'utiliser CROSS APPLY ?

J'ai lu (vaguement, par le biais de messages sur Internet) que cross apply peut être plus efficace lors de la sélection sur de grands ensembles de données si vous effectuez un partitionnement. (Paging vient à l'esprit)

Je sais aussi que CROSS APPLY _ { ne nécessite pas de fichier UDF comme table de droite.

Dans la plupart des requêtes INNER JOIN (relations un à plusieurs), je pouvais les réécrire pour utiliser CROSS APPLY, mais elles me donnaient toujours des plans d'exécution équivalents. 

Quelqu'un peut-il me donner un bon exemple de la manière dont CROSS APPLY fait la différence dans les cas où INNER JOIN fonctionnera également?


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
819
Jeff Meatball Yang

Quelqu'un peut-il me donner un bon exemple de la manière dont CROSS APPLY fait la différence dans les cas où INNER JOIN fonctionne également?

Voir l'article de mon blog pour une comparaison détaillée des performances:

CROSS APPLY fonctionne mieux sur les choses qui n'ont pas de simple condition JOIN.

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 ça en utilisant CTE 's et la fonction 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

, mais ceci est moins lisible et probablement moins efficace.

Mettre à jour:

Je viens de vérifier.

master est une table d'environ 20,000,000 enregistrements 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

s'exécute pendant presque 30 secondes, alors 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ée.

605
Quassnoi

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 , car, lorsqu'elles sont utilisées avec inner join, les fonctions de table ne peuvent prendre que variables ou constantes en tant que paramètres. (C'est-à-dire que le paramètre de fonction de table ne peut pas dépendre de la colonne d'une autre table.) 

Toutefois:

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 bien, syntaxe plus courte: (par ErikE)

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

Modifier:

Remarque: Informix 12.10 xC2 + contient Tableaux dérivés latéraux et Postgresql (9.3+) contient Sous-requêtes latérales qui peuvent être utilisés pour obtenir un effet similaire.

183
nurettin

Considérez que vous avez deux tables.

TABLEAU DE MAÎTRE

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

TABLEAU DÉTAILS

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 existe de nombreuses situations dans lesquelles nous devons remplacer INNER JOIN par CROSS APPLY

1. Joindre deux tables en fonction de TOP n résultats

Considérez si nous devons sélectionner Id et Name dans Master et les deux dernières dates pour chaque Id dans 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 la variable Id des deux dernières dates, puis a joint ces enregistrements uniquement 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 fonctionne. La requête dans CROSS APPLY peut faire référence à la table externe, où INNER JOIN ne peut pas le faire (une erreur de compilation est générée). Lors de la recherche des deux dernières dates, la jointure est effectuée dans CROSS APPLY, c.-à-d. WHERE M.ID=D.ID

2. Lorsque nous avons besoin de la fonctionnalité INNER JOIN à l'aide de fonctions.

CROSS APPLY peut être utilisé en remplacement de INNER JOIN lorsque nous devons obtenir le résultat de la table Master et une 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. CROSS APPLY ou OUTER APPLY peuvent être utilisés ici, qui sont interchangeables.

Considérez que 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)

ce qui vous amène 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
136
Sarath Avanavu

voici un exemple où CROSS APPLY fait une différence énorme en termes de performances:

Utilisation de CROSS APPLY pour optimiser les jointures sur des conditions BETWEEN

Notez qu'en plus de remplacer les jointures internes, vous pouvez également réutiliser du code, par exemple tronquer des dates sans pénaliser les performances liées à l'utilisation de fichiers UDF scalaires, par exemple: Calcul du troisième mercredi du mois avec des fichiers UDF en ligne

35
A-K

Il me semble que CROSS APPLY peut combler un certain vide lorsque vous utilisez des champs calculés dans des requêtes complexes/imbriquées, et les simplifie et les rend plus lisibles.

Exemple simple: vous avez un DoB et vous souhaitez présenter plusieurs champs liés à l'âge qui s'appuieront également sur d'autres sources de données (telles que l'emploi), telles que Age, Groupe d'âge, AgeAtHiring, MinimumRetirementDate, etc., à utiliser dans votre application d'utilisateur final. (Tableaux croisés dynamiques Excel, par exemple).

Les options sont limitées et rarement élégantes:

  • Les sous-requêtes JOIN ne peuvent pas introduire de nouvelles valeurs dans l'ensemble de données en fonction des données de la requête parente (elle doit être autonome).

  • Les FDU sont nets, mais lents car ils ont tendance à empêcher des opérations parallèles. Et être une entité séparée peut être une bonne chose (moins de code) ou une mauvaise chose (où est le code).

  • Tables de jonction. Parfois, ils peuvent fonctionner, mais assez vite, vous rejoignez des sous-requêtes avec des tonnes d'UNION. Grand désordre.

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

  • Tables intermédiaires. Oui ... cela fonctionne généralement, et est souvent une bonne option car ils peuvent être indexés rapidement, mais les performances peuvent également chuter du fait que les instructions UPDATE ne sont pas parallèles et ne permettent pas de mettre en cascade des formules (résultats de réutilisation) pour mettre à jour plusieurs champs de la liste même déclaration. Et parfois, vous préférez simplement faire les choses en un seul passage.

  • Imbrication de requêtes. Oui, à tout moment, vous pouvez placer des parenthèses sur l'ensemble de votre requête et l'utiliser comme sous-requête sur laquelle vous pouvez manipuler les données source et les champs calculés de la même manière. Mais vous ne pouvez le faire que trop souvent avant que la situation ne devienne laide. Très laid.

  • Code répété. Quelle est la plus grande valeur de 3 déclarations longues (CASE ... ELSE ... END)? Ça va être lisible!

    • Dites à vos clients de calculer eux-mêmes ces maudits choses.

Ai-je manqué quelque chose? Probablement, alors n'hésitez pas à commenter. Mais bon, CROSS APPLY est comme une aubaine dans de telles situations: vous ajoutez simplement une simple CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl et le tour est joué! 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 APPLY peuvent ...

  • être utilisé pour créer un ou plusieurs champs calculés sans ajouter de problèmes de performances, de complexité ou de lisibilité à l'ensemble
  • comme avec JOIN, plusieurs instructions CROSS APPLY ultérieures 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 un CROSS APPLY dans des conditions JOIN ultérieures
  • En prime, il y a l'aspect fonction de la valeur table

Dang, ils ne peuvent rien faire!

35
mtone

L'application croisée fonctionne également 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>

Utiliser 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
13
Chris

L'application Cross peut être utilisée pour remplacer la sous-requête lorsque vous avez besoin d'une colonne de la sous-requête. 

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 de société.

select P.*,T.CompanyName
from Person p
cross apply (
    select *
    from Company C
    where p.companyid = c.companyId and c.CompanyName like '%yyy%'
) T
5
balaji dileep kumar

Je suppose que ça devrait être la lisibilité;)

CROSS APPLY sera unique en son genre pour que les lecteurs lisent qu’une FDU est utilisée, qui sera appliquée à chaque rangée du tableau de gauche.

Bien sûr, il existe d'autres limitations dans lesquelles l'application CROSS est mieux utilisée que JOIN que d'autres amis ont postée ci-dessus.

4
shahkalpesh

Voici un article qui explique tout, avec leur différence de performance et leur utilisation sur JOINS.

SQL Server CROSS APPLY et EXTER APPLY sur JOINS

Comme suggéré dans cet article, il n'y a pas de différence de performances entre elles pour les opérations de jointure normales (INNER ET CROSS).

 enter image description here

La différence d'utilisation survient lorsque 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 avec INNER JOIN, ce qui vous donnerait l'erreur "L'identifiant en plusieurs parties" D.DepartmentID "ne peut pas être lié." Ici, la valeur est transmise à la fonction en tant que chaque ligne est lue. Ça a l'air cool pour moi. :)

3
Shanid

Cela a déjà été très bien répondu techniquement, mais laissez-moi vous donner un exemple concret de son utilité:

Disons que vous avez deux tables, Client et Commande. Les clients ont beaucoup de commandes.

Je souhaite créer une vue qui me donne des détails sur les clients et la dernière commande passée. Avec juste JOINS, cela nécessiterait des auto-jointures et une agrégation qui n’est pas jolie. Mais avec Cross Apply, c'est super facile:

SELECT *
FROM Customer
CROSS APPLY (
  SELECT TOP 1 *
  FROM Order
  WHERE Order.CustomerId = Customer.CustomerId
  ORDER BY OrderDate DESC
) T
2
Apneal

Eh bien, je ne sais pas si cela constitue une raison d'utiliser Cross Apply ou Inner Join, mais cette question a été traitée pour moi dans un message de forum utilisant Cross Apply. Je ne suis donc pas sûr qu'il existe une méthode equalivent utilisant Inner Join:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

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

AS COMMENCE

-- 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

FIN

2
user1054326

L'essence de l'opérateur APPLY est de permettre la corrélation entre les côtés gauche et droit de l'opérateur dans la clause FROM.

Contrairement à JOIN, la corrélation entre les entrées n'est pas autorisée.

En parlant de corrélation dans l’opérateur APPLY, je veux dire sur le côté droit, nous pouvons mettre:

  • une table dérivée - en tant que sous-requête corrélée avec un alias 
  • une fonction de valeur de table - une vue conceptuelle avec des paramètres, où le paramètre peut faire référence au côté gauche

Les deux peuvent renvoyer plusieurs colonnes et lignes.

0
Raf

C'est peut-être une vieille question, mais j'aime toujours le pouvoir de CROSS APPLY de simplifier la réutilisation de la logique et de fournir un mécanisme "d'enchaînement" des résultats.

J'ai fourni un code SQL Fiddle 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 rien ne devienne désordonné. Il n'est pas difficile d'extrapoler ici des calculs plus complexes.

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

0
mrmillsy