J'écris sur un article de blog à venir sur les fonctions de classement et d'agrégation de fenêtres, en particulier les itérateurs de Segment and Sequence Project. D'après ce que je comprends, Segment identifie les lignes d'un flux qui constituent la fin / le début d'un groupe, donc la requête suivante:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
Utilisera Segment pour savoir quand une ligne appartient à un groupe différent de la ligne précédente. L'itérateur Sequence Project effectue ensuite le calcul du numéro de ligne réel, sur la base de la sortie de la sortie de l'itérateur Segment.
Mais la requête suivante, utilisant cette logique, ne devrait pas avoir à inclure un segment, car il n'y a pas d'expression de partition.
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
Cependant, lorsque j'essaie cette hypothèse, ces deux requêtes utilisent un opérateur de segment. La seule différence est que la deuxième requête n'a pas besoin d'un GroupBy
sur le segment. Cela n'élimine-t-il pas la nécessité d'un segment en premier lieu?
Exemple
CREATE TABLE dbo.someTable (
someGroup int NOT NULL,
someOrder int NOT NULL,
someValue numeric(8, 2) NOT NULL,
PRIMARY KEY CLUSTERED (someGroup, someOrder)
);
--- Query 1:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
FROM dbo.someTable;
--- Query 2:
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
FROM dbo.someTable;
la source
<GroupBy />
donc le segment ne fait vraiment rien, presque, il sort la colonne du segment à l'opérateur Sequence Project. La raison pour laquelle l'opérateur de segment doit être présent pourrait être que l'opérateur de projet de séquence a besoin de cette valeur pour effectuer son travail.Réponses:
J'ai trouvé ce billet de blog de 6 ans mentionnant le même comportement.
Il semble
ROW_NUMBER()
toujours inclure un opérateur de segment, qu'ilPARTITION BY
soit utilisé ou non. Si je devais deviner, je dirais que c'est parce que cela facilite la création d'un plan de requête sur le moteur.Si le segment est nécessaire dans la plupart des cas, et dans les cas où il n'est pas nécessaire, il s'agit essentiellement d'une non-opération à coût nul, il est beaucoup plus simple de l'inclure toujours dans le plan lorsqu'une fonction de fenêtrage est utilisée.
la source
Selon le showplan.xsd du plan d'exécution,
GroupBy
apparaît sansminOccurs
ou lesmaxOccurs
attributs qui par défaut sont donc [1..1] rendant l'élément obligatoire, pas nécessairement le contenu. L'élément enfantColumnReference
de type (ColumnReferenceType
) aminOccurs
0 etmaxOccurs
[0 .. *] illimité, ce qui le rend facultatif , d'où l'élément vide autorisé. Si vous essayez manuellement de supprimerGroupBy
et de forcer le plan, vous obtenez l'erreur attendue:Fait intéressant, j'ai trouvé que vous pouvez supprimer manuellement l'opérateur de segment pour obtenir un plan de forçage valide qui ressemble à ceci:
Cependant, lorsque vous exécutez avec ce plan (en utilisant
OPTION ( USE PLAN ... )
), l'opérateur de segment réapparaît comme par magie. Va juste pour montrer que l'optimiseur ne prend que les plans XML comme guide approximatif.Mon banc d'essai:
Découpez le plan XML du banc de test et enregistrez-le en tant que .sqlplan pour afficher le plan moins le segment.
PS Je ne passerais pas trop de temps à découper manuellement les plans SQL, comme si vous me connaissiez, vous saviez que je le considère comme un travail fastidieux et quelque chose que je ne ferais jamais. Oh attends!? :)
la source