J'ai un script simple qui obtient quatre nombres aléatoires (1 à 4), puis se joint à nouveau pour obtenir le numéro de database_id correspondant. Lorsque j'exécute le script avec un LEFT JOIN, j'obtiens à chaque fois quatre lignes (le résultat attendu). Cependant, lorsque je l'exécute avec une INNER JOIN, j'obtiens un nombre variable de lignes - parfois deux, parfois huit.
Logiquement, il ne devrait pas y avoir de différence car je sais que des lignes avec database_ids 1-4 existent dans sys.databases. Et parce que nous sélectionnons dans la table des nombres aléatoires avec quatre lignes (par opposition à la joindre à elle), il ne devrait jamais y avoir plus de quatre lignes retournées.
Cela se produit à la fois dans SQL Server 2012 et 2014. Qu'est-ce qui pousse INNER JOIN à retourner un nombre variable de lignes?
/* Works as expected -- always four rows */
SELECT rando.RandomNumber, d.database_id
FROM
(SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber
FROM sys.databases WHERE database_id <= 4) AS rando
LEFT JOIN sys.databases d ON rando.RandomNumber = d.database_id;
/* Returns a varying number of rows */
SELECT rando.RandomNumber, d.database_id
FROM
(SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber
FROM sys.databases WHERE database_id <= 4) AS rando
INNER JOIN sys.databases d ON rando.RandomNumber = d.database_id;
/* Also returns a varying number of rows */
WITH rando AS (
SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber
FROM sys.databases WHERE database_id <= 4
)
SELECT r.RandomNumber, d.database_id
FROM rando AS r
INNER JOIN sys.databases d ON r.RandomNumber = d.database_id;
la source
SELECT TOP (4) d.database_id FROM sys.databases AS d CROSS JOIN (VALUES (1),(2),(3),(4)) AS multi (i) WHERE d.database_id <= 4 ORDER BY CHECKSUM(NEWID()) ;
je suppose que cela fonctionne bien car il n'y a pas de jointure sur la valeur de la fonction non déterministe.Réponses:
En ajoutant le SELECT supplémentaire, il pousse l'évaluation scalaire de calcul plus profondément dans le plan et donne le prédicat de jointure, le scalaire de calcul en haut fait ensuite référence à la précédente.
Je cherche toujours à savoir pourquoi il attend si tard pour le faire, mais je lis actuellement cet article de Paul White ( https://sql.kiwi/2012/09/compute-scalars-expressions-and-execution-plan-performance.html ) . Peut-être que cela a à voir avec le fait que NEWID n'est pas déterministe?
la source
Cela pourrait donner un aperçu jusqu'à ce que l'une des personnes les plus intelligentes du site se lance.
Je mets les résultats aléatoires dans une table temporaire et j'obtiens systématiquement 4 résultats quel que soit le type de jointure.
Si je compare les plans de requête entre votre deuxième requête et la variation avec une variable de table, je peux voir qu'il y a une nette différence entre les deux. Le X rouge est
No Join Predicate
tellement étrange pour mon développeur de l'homme des cavernesSi j'élimine le bit aléatoire de la requête à une constante
1 % (4)
, mon plan semble meilleur mais le calcul scalaire a été éliminé, ce qui m'a amené à regarder de plus prèsIl calcule l'expression du nombre aléatoire après la jointure. Que ce soit prévu, je laisse toujours aux assistants internes sur le site, mais au moins c'est pourquoi vous obtenez des résultats variables dans votre jointure.
2014
Pour ceux qui jouent à la maison, les plans de requête ci-dessus ont été générés à partir d'une instance 2008 R2. Les plans de 2014 sont différents, mais l'opération Compute Scalar reste après la jointure.
Ceci est le plan de requête pour un 2014 en utilisant l'expression constante
Il s'agit du plan de requête pour une instance 2014 utilisant l'expression newid.
Apparemment, c'est par conception, problème de connexion ici. Merci à @paulWhite de savoir que cela existait.
la source