Je suis tombé sur cette question sur un conversation Twitter avec Lukas Eder .
Bien que le comportement correct serait 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?
La plupart des bases de données sont assez claires sur le fait qu'un ORDER BY
Dans une sous-requête est soit:
TOP
ou OFFSET .. FETCH
)OFFSET .. FETCH
Ou LIMIT
)Voici un exemple tiré du manuel DB2 LUW (accent sur le mien)
Une clause ORDER BY dans une sous-sélection n'affecte pas l'ordre des lignes renvoyé 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 clause ORDER BY
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 il serait imprudent de s’y fier.
En particulier, DB2 a une fonction moins connue appelée ORDER 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
Pendant des années, il a été une pratique dans Oracle pour implémenter OFFSET
pagination en utilisant ROWNUM
, qui peut être raisonnablement calculé uniquement après ordonnant 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 de ROWNUM
dans une requête, les futures versions d'Oracle ne brisent pas ce comportement afin de ne pas briser à peu près tout le SQL Oracle existant, qui n'a pas encore migré à la syntaxe SQL OFFSET .. FETCH
SQL beaucoup plus souhaitable et lisible:
SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
Oui. Sans ORDER BY
clause 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 un ORDER BY
clause à l'endroit approprié.
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.
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 Postgres (que vous testiez) actuellement existantes 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 ORDER BY
Dans les sous-requêtes. L'ordre de tri est néanmoins garanti pour cette simple requête dans Postgres. ORDER BY
Est 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
Au SELECT
extérieur. (Dans ce cas, le ORDER BY
Interne serait un bruit redondant pour cette simple requête.)