Question

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

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

Je sais aussi que CROSS APPLY ne nécessite pas une UDF que le droit table .

Dans la plupart des requêtes INNER JOIN (un à-plusieurs), je pourrais les réécrire à utiliser CROSS APPLY, mais ils me donnent toujours des plans d'exécution équivalents.

Quelqu'un peut-il me donner un bon exemple quand CROSS APPLY fait une différence dans les cas où INNER JOIN fonctionnera aussi bien?


Modifier

Voici un exemple trivial, où les plans d'exécution sont exactement les mêmes. (Montrez-moi 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
Était-ce utile?

La solution

  

Quelqu'un peut-il me donner un bon exemple quand CROSS APPLY fait une différence dans les cas où INNER JOIN fonctionnera aussi bien?

Voir l'article dans mon blog pour la comparaison détaillée des performances:

CROSS APPLY fonctionne mieux sur des choses qui n'ont pas de condition simple 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 de INNER JOIN.

Vous pourriez probablement faire quelque chose comme ça en utilisant la fonction de la CTE et la 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 cela est moins lisible et probablement moins efficace.

Mise à jour:

Juste vérifiés.

master est une table d'environ enregistrements 20,000,000 avec 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 quelques secondes presque 30, 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.

Autres conseils

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

Ceci est un erreur de syntaxe , parce que, lorsqu'il est utilisé avec inner join, les fonctions de table ne peuvent prendre variables ou constantes en tant que paramètres. (À savoir, le paramètre de la fonction de table 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

est légal.

Edit: Ou bien, la 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

Note: Informix 12.10 xC2 + a Tables latérales dérivées et Postgresql (9.3+) a Les sous-requêtes latérales qui peuvent être utilisés pour un effet similaire.

Considérez que vous avez deux tables.

TABLE MASTER

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

Détails 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 avec CROSS APPLY.

1. Joignez-vous à deux tables en fonction des résultats de TOP n

Considérez si nous devons sélectionner Id et Name de Master et deux 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 produit des résultats pour deux dates avec le Id de deux dernières date et a ensuite rejoint ces enregistrements que dans la requête externe sur Id, ce qui est faux. Pour cela, 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 cela fonctionne. La requête à l'intérieur CROSS APPLY peut faire référence à la table externe, où INNER JOIN ne peut le faire (il lance erreur de compilation). Lors de la recherche des deux dernières dates, assemblage est fait à l'intérieur CROSS APPLY à savoir WHERE M.ID=D.ID.

2. Lorsque nous avons besoin d'une fonctionnalité de INNER JOIN en utilisant les fonctions.

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

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

est la fonction ici

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 de CROSS APPLY

APPLY peut être utilisé pour remplacer UNPIVOT. Soit CROSS APPLY ou OUTER APPLY peuvent être utilisés ici, qui sont interchangeables.

Considérez que vous avez le tableau ci-dessous (du nom de 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 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

Il me semble que CROSS APPLY peut remplir un certain écart lorsque vous travaillez avec des champs calculés dans les requêtes complexes / imbriquées, et les rendre plus simple et plus lisible.

Exemple simple: vous avez un ddn et vous souhaitez présenter plusieurs domaines liés à l'âge qui sera également compter sur d'autres sources de données (par exemple l'emploi), comme l'âge, agegroup, AgeAtHiring, MinimumRetirementDate, etc. pour une utilisation dans votre fin l'application -user (Excel PivotTables, par exemple).

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

  • REJOIGNEZ les sous-requêtes ne peuvent pas introduire de nouvelles valeurs dans l'ensemble de données à partir des données de la requête parent (il doit se suffire à lui-même).

  • UDFs sont propres, mais lent car ils ont tendance à empêcher les opérations parallèles. Et étant une entité distincte peut être un bon (moins de code) ou mauvais (où est le code) chose.

  • tables de jonction. Parfois, ils peuvent travailler, mais assez vite vous rejoignez avec des tonnes de sous-requêtes UNIONs. Big désordre.

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

  • tables intermédiaires. Oui ... qui fonctionne habituellement, et souvent une bonne option, car ils peuvent être indexés et rapide, mais la performance peut aussi diminuer en raison de d'actualiser ces déclarations ne sont parallèles et ne permettant pas de formules en cascade (résultats de réutilisation) pour mettre à jour plusieurs champs dans la même déclaration. Et vous préférez simplement parfois faire des choses en une seule passe.

  • requêtes Nesting. Oui, à tout moment, vous pouvez mettre entre parenthèses sur l'ensemble de votre requête et l'utiliser comme une sous-requête sur laquelle vous pouvez manipuler des données source et les champs calculés aussi bien. Mais vous ne pouvez le faire tellement avant qu'il ne soit laid. Très laid.

  • Code de répétition. Quelle est la plus grande valeur de 3 longues déclarations (CASE ... ELSE ... END)? Ce va y avoir lisible!

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

Ai-je raté quelque chose? Probablement, alors ne hésitez pas à commenter. Mais bon, CROSS APPLY est comme un don du ciel dans de telles situations: vous ajoutez un 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 a toujours été là dans vos données source.

Les valeurs introduites par CROSS APPLY peut ...

  • être utilisé pour créer un ou plusieurs champs calculés sans ajouter des problèmes de performance, la complexité ou la lisibilité du mélange
  • comme avec JOIN, plusieurs CROSS après les déclarations POSTULER peuvent se référer à eux-mêmes: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • vous pouvez utiliser des valeurs introduites par une conditionnalité en vigueur dans la suite JOIN
  • En prime, il y a l'aspect de la fonction de valeur table

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

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

Par exemple, si vous avez une table contenant une xml

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

Utilisation 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

renvoie un résultat

xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1     test1            1                    Benefits
1     test1            4                    FINRPTCOMPANY

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

Disons que vous avez deux tables, Customer et Order. Les clients ont beaucoup de commandes.

Je veux créer une vue qui me donne des détails sur les clients, et le plus récent ordre dans lequel ils ont fait. Avec juste REJOINT, cela nécessiterait une certaine auto-joint et l'agrégation qui n'est pas assez. Mais avec la Croix Appliquer, son super facile:

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

Cross appliquer peut être utilisé pour remplacer du sous-requête où 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 vais pas être en mesure de sélectionner les colonnes du tableau de la société donc, en utilisant croix 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

Je suppose que ce doit être la lisibilité;)

CROSS sera APPLIQUER un peu unique pour les gens qui lisent pour leur dire que l'UDF est utilisé qui sera appliqué à chaque ligne de la table à gauche.

Ofcourse, il y a d'autres limites où CROSS APPLY est mieux utilisé que REJOIGNEZ que d'autres amis ont posté ci-dessus.

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

SQL Server CROSS APPLY et OUTER APPLY sur SE JOINT

Comme suggéré dans cet article, il n'y a pas de différence de performance entre eux pour se joindre à la normale des opérations (CROSS ET INNER).

La différence d'utilisation arrive lorsque vous avez à faire une requête comme ceci:

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, quand vous devez relier avec la fonction. Cela ne peut pas être fait en utilisant INNER JOIN, qui vous donnera l'erreur « L'identifiant multi-partie « D.DepartmentID » ne pouvait pas être lié. » Ici, la valeur est passée à la fonction que chaque rangée est lue. Sons cool pour moi. :)

Eh bien, je ne suis pas sûr si cela est considéré comme une raison d'utiliser Cross Appliquer par rapport jointure interne, mais cette requête a été répondu pour moi dans un forum post en utilisant Cross Appliquer, donc je ne sais pas s'il existe une méthode de equalivent à l'aide intérieure Joignez-vous à:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

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

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

FIN

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 est pas autorisé.

En parlant de corrélation à l'opérateur d'appliquer, je veux dire sur le côté droit, nous pouvons mettre:

  • une table dérivée - sous forme de sous-requête corrélée avec un alias
  • une fonction d'une valeur de table - une vue conceptuelle avec des paramètres, où le paramètre peut se référer à la partie gauche

Les deux peuvent retourner plusieurs colonnes et lignes.

Ceci est peut-être une vieille question, mais je l'aime toujours la puissance de CROSS APPLY pour simplifier la réutilisation de la logique et de fournir un mécanisme de « chaînage » des 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 du tout désordre. Il n'est pas difficile d'extrapoler à partir des calculs plus complexes ici.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top