web-dev-qa-db-fra.com

CROSS APPLY vs OUTER APPLY différence de vitesse

J'utilisais CROSS APPLY pour rejoindre les utilisateurs et les tables GeoPhone et tout fonctionnait rapidement, mais maintenant j'ai des utilisateurs avec des valeurs NULL dans la colonne Téléphone. L'application croisée ignore ces lignes dans la sortie finale. Je suis donc passé à OUTER APPLY. Mais cela fonctionne beaucoup plus lentement (plus de 15 fois plus lentement lorsque le nombre total de lignes en sortie n'a augmenté que de 1000).

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone

Contre:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users OUTER APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone

J'essaie de comprendre pourquoi. Comme je le vois, le plan d'exécution est différent. Mais théoriquement, je ne vois aucun calcul pouvant provoquer un tel ralentissement.

Des idées?

MA SOLUTION FINALE:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE ISNULL(dbo.Users.Phone, 0) <= dbo.GeoPhone.[End]) GeoPhone

Cela affecte le pays réel pour les téléphones non nuls et le pays de la première plage pour les téléphones nuls (qui est déjà "INCONNU" pour mon cas). Pour certaines raisons WHERE dbo.Users.Phone <= dbo.GeoPhone.[End] OR dbo.Users.Phone IS NULL donne les mêmes résultats mais beaucoup plus lentement.

N'hésitez pas à commenter cela.

51
Denis

CROSS APPLY est spécifique à MSSQL ... Microsoft on APPLY

APPLY fait exécuter la requête de droite une fois par résultat dans la requête de gauche. CROSS considère uniquement les lignes correspondantes comme INNER JOIN. L'utilisation d'OUTER considère toutes les lignes de la requête de gauche. Les rangs supplémentaires font mal.

Je vous recommande de reformuler votre requête de droite pour accepter explicitement les valeurs NULL au lieu d'utiliser OUTER APPLY.

73
Magicianeer

Vous pouvez essayer ceci:

SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, GeoPhone.Country
FROM  dbo.Users CROSS APPLY
                 (SELECT TOP 1 Country
                 FROM    dbo.GeoPhone
                 WHERE dbo.Users.Phone <= dbo.GeoPhone.[End]) GeoPhone
UNION ALL
SELECT TOP (10000) dbo.Users.Login, dbo.Users.Phone, NULL AS Country
FROM  dbo.Users
WHERE dbo.Users.Phone IS NULL

Assurez-vous d'avoir un index sur dbo.Users.Phone

11
A-K