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
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.
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.
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
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
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!
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 ...
CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
Dang, ils ne peuvent rien faire!
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
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
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.
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).
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. :)
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
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
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:
Les deux peuvent renvoyer plusieurs colonnes et lignes.
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.