Est-il VRAIMENT possible que la commande ne soit pas garantie pour cette table dérivée redondante particulière?

12

Je suis tombé sur cette question lors d'une conversation Twitter avec Lukas Eder .

Bien que le comportement correct soit d'appliquer la clause ORDER BY à la requête la plus externe, car, ici, nous n'utilisons pas DISTINCT, GROUP BY, JOIN ou toute autre clause WHERE dans la requête la plus externe, pourquoi un SGBDR ne passerait-il pas simplement la les données entrantes telles qu'elles ont été triées par la requête interne?

SELECT * 
FROM (
    SELECT * FROM table ORDER BY time DESC
) AS t

Lorsque vous exécutez cet exemple sur PostgreSQL, au moins, vous obtenez le même plan d'exécution pour la requête interne et cet exemple de table dérivée, ainsi que le même jeu de résultats.

Donc, je suppose que le planificateur supprimera simplement la requête la plus externe car elle est redondante ou passera simplement par les résultats de la table interne.

Quelqu'un pense-t-il que ce n'est pas le cas?

Vlad Mihalcea
la source
4
Notez que votre requête échouera dans SQL Server car une commande par n'est pas autorisée dans une table dérivée.
a_horse_with_no_name
Pourquoi es-tu si incrédule? Pourquoi supposeriez-vous quelque chose? Lorsque vous écrivez un programme qui vous laisse le choix, vous attendez-vous à ce que les utilisateurs s’attendent à des choses sur votre choix? Lisez à propos de l'optimisation / implémentation logique et physique des requêtes.
philipxy
2
"Je suppose que le planificateur supprimera simplement la requête la plus externe car elle est redondante ou passera simplement par les résultats de la table interne." Vous pouvez tout aussi bien supposer que le planificateur supprimera la clause de classement de la requête interne car elle n'a pas de sens dans le contexte.
Wildcard
MariaDB, vers 2012, discute de la question. Absence deORDER BYplombinternepour une optimisation différente pour le groupe max .
Rick James
1
En fait, vous avez raison pour Postgres.
Erwin Brandstetter

Réponses:

20

La plupart des bases de données sont assez claires sur le fait qu'un ORDER BYdans une sous-requête est soit:

  • Non autorisé: par exemple, SQL Server, Sybase SQL Anywhere (sauf si complété TOPou OFFSET .. FETCH)
  • Inutile: par exemple PostgreSQL, DB2 (encore une fois, sauf si complété avec OFFSET .. FETCHou LIMIT)

Voici un exemple tiré du manuel DB2 LUW (c'est moi qui souligne)

Une clause ORDER BY dans une sous-sélection n'affecte pas l'ordre des lignes renvoyées par une requête. Une clause ORDER BY n'affecte l'ordre des lignes renvoyées que si elle est spécifiée dans le fullselect le plus externe.

La formulation est assez explicite, tout comme celle de PostgreSQL :

Si le tri n'est pas choisi, les lignes seront retournées dans un ordre non spécifié. L'ordre réel dans ce cas dépendra des types de plan d'analyse et de jointure et de l'ordre sur le disque, mais il ne faut pas s'y fier . Un ordre de sortie particulier ne peut être garanti que si l'étape de tri est explicitement choisie.

D'après cette spécification, il peut être suivi que tout ordre résultant de la ORDER BYclause dans une table dérivée est simplement accidentel et peut coïncider par hasard avec votre ordre attendu (ce qu'il fait dans la plupart des bases de données dans votre exemple trivial), mais il serait imprudent de s'appuyer sur cette.

Note latérale sur DB2:

En particulier, DB2 a une fonction moins connue appeléeORDER BY ORDER OF <table-designator> , qui peut être utilisée comme suit:

SELECT C1 FROM
   (SELECT C1 FROM T1
      UNION
    SELECT C1 FROM T2
    ORDER BY C1 ) AS UTABLE
ORDER BY ORDER OF UTABLE

Dans ce cas particulier, l'ordre de la table dérivée peut être explicitement réutilisé dans le plus externe SELECT

Note complémentaire sur Oracle:

Pendant des années, il a été une pratique dans Oracle pour implémenter la OFFSETpagination à l'aide ROWNUM, qui ne peut être raisonnablement calculée qu'après avoir commandé une table dérivée:

SELECT *
FROM (
  SELECT rownum AS rn, t.* -- ROWNUM here depends on the derived table's ordering
  FROM (
    SELECT * FROM table ORDER BY time DESC
  ) t
) t
WHERE rn BETWEEN 10 AND 20

On peut raisonnablement s'attendre à ce qu'au moins en présence d' ROWNUMune requête, les futures versions d'Oracle ne cassent pas ce comportement afin de ne pas casser à peu près tout l'héritage Oracle SQL, qui n'a pas encore migré vers le plus souhaitable et OFFSET .. FETCHsyntaxe standard lisible de SQL :

SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
Lukas Eder
la source
Meaningless: E.g. PostgreSQLdevrait vraiment être: « peu fiables », parce qu'il fait quelque chose de méchant. Les lignes sont triées dans la requête interne et cet ordre est conservé dans les niveaux de requête externes, sauf indication contraire ou si la réorganisation est opportune pour des opérations supplémentaires. Même si ce n'est qu'un détail d'implémentation, il n'est pas dénué de sens. Cela peut être utilisé pour une entrée triée pour agréger des fonctions. Le manuel laisse même entendre autant: Alternatively, supplying the input values from a sorted subquery will usually work.
Erwin Brandstetter
La citation que vous avez ajoutée pour Postgres s'applique en fait à un autre cas: les requêtes sans aucun ORDER BY.
Erwin Brandstetter
@ErwinBrandstetter: N'hésitez pas à ajouter une réponse avec ces détails. Personnellement, je ne suis pas d'accord pour dire que les détails de mise en œuvre sont significatifs. Aujourd'hui même, j'ai appris que dans le passé, les gens comptaient sur Oracle pour effectuer toujours un groupe trié par opération dans Oracle 8i (je crois), quand soudain, une version plus récente a introduit le groupe haché par, ce qui a brisé l'hypothèse que certains implicites la commande peut être invoquée. En d'autres termes: j'aime le mettre en gras. Inutile , plutôt que oh si vous connaissez les détails complexes de la version xyz, vous pouvez réellement ...
Lukas Eder
J'ai déjà ajouté une réponse. Que nous choisissions d'ignorer un comportement non standard ou quels autres bons conseils nous avons à côté de la question: l' ordre est-il garanti pour la requête donnée? C'est pour Postgres. Il n'est pas (ou même pas applicable) pour les autres SGBDR. Et cela s'applique à toutes les versions existantes de Postgres, pas seulement à la version xyz. Elle est même documentée (avec des réserves). Votre devis est trompeur. Si nous voulons ignorer un comportement non standard, nous pourrions commencer par Oracle en nous faisant croire que NULL et la chaîne vide sont les mêmes. Également orthogonale à la question.
Erwin Brandstetter
@ErwinBrandstetter: Intéressant, merci pour la mise à jour. Cette garantie à laquelle vous faites référence est-elle documentée?
Lukas Eder
12

Oui. Sans ORDER BYclause, l'ordre de sortie n'est pas défini et le planificateur de requêtes est tout à fait à sa portée de supposer que vous le savez et le comprenez.

Il peut décider que parce que la requête externe ne spécifie pas d'ordre, il peut supprimer l'ordre dans la requête interne pour éviter une opération de tri, surtout s'il n'y a pas d'index cluster ou pas d'index du tout pour prendre en charge l'ordre. Si ce n'est pas le cas maintenant, cela pourrait le faire dans les futures versions.

Ne vous fiez jamais à un comportement indéfini. Si vous avez besoin d'une commande spécifique, donnez une ORDER BYclause à l'endroit approprié.

David Spillett
la source
Lors du test sur PostgreSQL, le tri a été effectué après un scan séquentiel car je n'avais aucun index sur la colonne utilisée par ORDER BY. Selon vous, quel SGBDR ignorera la requête interne ORDER BY?
Vlad Mihalcea
5
Je ne peux pas dire que je sais tout ce qui va , juste qu'ils sont tous et tout parfaitement libre de le faire si elles le souhaitent - ce serait une optimisation parfaitement acceptable selon les normes générales et les spécifications du produit. SQL Server rejettera la requête de manière définitive (sauf si vous l'incluez TOP 100%afin que la requête actuelle ne soit pas portable, si cela doit être une priorité pour votre projet. Parce que Postgres obéit à l'ordre dans la requête interne maintenant n'implique pas qu'il le fera toujours à l'avenir (ou que les anciennes versions font, en fait), vous devriez donc éviter de vous fier au comportement au cas où.
David Spillett
1
@VladMihalcea un SGBD qui "optimise" le redondant ORDER BYest MariaDB: Pourquoi ORDER BY dans une sous-requête FROM est-il ignoré?
ypercubeᵀᴹ
6

C'est le problème même avec un comportement indéfini - fonctionne pour vous, fonctionne pour moi, reformate le disque dur en prod;)

Nous pouvons prendre du recul et dire que dans un sens, vous avez raison - il n'y a aucune raison terrestre pour laquelle un SGBDR sensé réorganiserait les lignes dans la sélection intérieure. Mais ce n'est pas garanti - ce qui signifie qu'il peut y avoir à l'avenir une raison, et les vendeurs sont libres de le faire. Cela signifie que tout code qui s'appuie sur ce comportement est à la merci d'un changement qu'un fournisseur pourrait apporter et qu'il n'aurait aucune obligation de publier, car il ne s'agit pas d'un changement de rupture par rapport à un PDV API.

PaulJWilliams
la source
2
La seule raison pour laquelle il peut optimiser la commande est la vitesse. Le retour des lignes dans un ordre différent peut être plus efficace.
TomTom
2
En particulier, le serveur peut exploiter le parallélisme pour lire le tableau. Si c'est le cas, et qu'il n'est pas nécessaire d'exécuter une commande, vous récupérerez les lignes, mais les threads les liront. (SQL Server fait cela, de sorte qu'un a SELECTsans ORDER BYvraiment n'est pas déterministe, et pas seulement en théorie ou parce que les données ont changé.)
Jeroen Mostert
@JeroenMostert: Un comportement indéfini ne fait qu'empirer. Que se passe-t-il s'il est hors service et que le delta a été utilisé pour indexer dans un tableau?
Joshua
2

Est-il VRAIMENT possible que la commande ne soit pas garantie pour cette table dérivée redondante particulière?

La réponse pour toutes les versions existantes de Postgres (que vous testiez) est: Non - pour cette requête particulière. L'ordre de tri est garanti.

Les gens du serveur SQL ne seront pas à l'aise avec cela car Microsoft n'autorise même pas les ORDER BYsous-requêtes. L'ordre de tri est néanmoins garanti pour cette simple requête dans Postgres. ORDER BYest appliqué dans la sous-requête et la requête externe ne fait rien qui puisse changer l'ordre.

Le manuel l'indique même dans le chapitre Fonctions d'agrégation :

Alternativement, la fourniture des valeurs d'entrée à partir d'une sous-requête triée fonctionne généralement.

Notez que cela n'est vrai que lorsque les niveaux de requête externes n'ajoutent pas d'opérations susceptibles de modifier l'ordre. Il n'est donc "garanti" que pour le cas simple, et ce n'est pas soutenu par la norme SQL. Postgres est libre de réorganiser si cela est opportun pour des opérations supplémentaires. En cas de doute, ajoutez un autre ORDER BYà l'extérieur SELECT. (Dans ce cas, le ORDER BYbruit interne serait redondant pour cette simple requête.)

Erwin Brandstetter
la source
Est-ce vrai lorsque le "table"n'est pas une simple table de base mais une vue complexe ou une table partitionnée? Est-ce vrai également lorsque le plan est exécuté en parallèle? Est-ce vrai aussi dans Postgres 10? (Je demande seulement, je ne suis pas sûr de la réponse à aucune de ces questions.)
ypercubeᵀᴹ
@ ypercubeᵀᴹ: Je n'ai pas testé Postgres 10 pour tout cela, mais je suis sûr que c'est vrai dans tous les cas. L'ordre est appliqué et non modifié dans la requête externe pour le cas simple.
Erwin Brandstetter