Les fonctions scalaires peuvent-elles être appliquées avant le filtrage lors de l'exécution d'une instruction SQL?

Je suppose que j'ai toujours naïvement supposé que les fonctions scalaires dans la partie select d'une requête SQL ne seront appliquées qu'aux lignes qui répondent à tous les critères de la clause where.

Aujourd'hui, j'étais en train de déboguer du code d'un fournisseur et j'avais cette hypothèse contestée. La seule raison pour laquelle je peux penser à l'échec de ce code est que la fonction Subssortingng () est appelée sur des données qui auraient dû être filtrées par la clause WHERE. Mais il semble que l'appel de la sous-string est appliqué avant le filtrage, la requête échoue. Voici un exemple de ce que je veux dire. Disons que nous avons deux tables, chacune avec 2 colonnes et ayant 2 lignes et 1 rangée respectivement. La première colonne de chaque est juste un identifiant. NAME est juste une string, et NAME_LENGTH nous indique combien de caractères dans le nom avec le même ID. Notez que seuls les noms comportant plusieurs caractères ont une ligne correspondante dans la table LONG_NAMES.

NAMES: ID, NAME 1, "Peter" 2, "X" LONG_NAMES: ID, NAME_LENGTH 1, 5 

Si je veux une requête pour imprimer chaque nom avec les 3 dernières lettres coupées, je pourrais d'abord essayer quelque chose comme ceci (en supposant la syntaxe de SQL Server pour l'instant):

 SELECT subssortingng(NAME,1,len(NAME)-3) FROM NAMES; 

Je découvrirais bientôt que cela me donnerait une erreur, parce que quand il atteint "X" il essayera d'employer un nombre négatif pour dans l'appel de sous-string, et il échouera. La façon dont mon fournisseur a décidé de résoudre ce problème était de filterr les lignes où les strings étaient trop courtes pour que la requête len-3 fonctionne. Il l'a fait en se joignant à une autre table:

 SELECT subssortingng(NAMES.NAME,1,len(NAMES.NAME)-3) FROM NAMES INNER JOIN LONG_NAMES ON NAMES.ID = LONG_NAMES.ID; 

À première vue, cette requête semble fonctionner. La condition de jointure éliminera toutes les lignes ayant des champs NAME suffisamment courts pour que l'appel de sous-string échoue.

Cependant, d'après ce que je peux observer, SQL Server va parfois essayer de calculer l'expression de la sous-string pour tout dans la table, puis appliquer la jointure pour filterr les lignes. Est-ce censé se produire de cette façon? Y a-t-il un ordre des opérations documenté où je peux savoir quand certaines choses vont se passer? Est-il spécifique à un moteur de database particulier ou à une partie de la norme SQL? Si j'ai décidé d'inclure un prédicat sur ma table NAMES pour filterr les noms courts, (comme len (NAME)> 3), SQL Server pourrait-il également choisir d'appliquer cela après avoir essayé d'appliquer la sous-string? Si oui, alors il semble que le seul moyen sûr de faire une sous-string serait de l'envelopper dans une construction "case when" dans le select?

Martin a donné ce lien qui explique à peu près ce qui se passe – l'optimiseur de requête a toute latitude pour réorganiser les choses comme bon lui semble. J'inclus ceci comme réponse pour que je puisse accepter quelque chose. Martin, si vous créez une réponse avec votre lien, j'accepterai volontiers cela au lieu de celui-ci.

Je veux laisser ma question ici parce que je pense que c'est une question délicate à searchr, et ma formulation particulière de la question pourrait être plus facile à find pour quelqu'un d'autre à l'avenir.

TSQL divide par zéro rencontré malgré aucune colonne contenant 0

EDIT: Comme plus de réponses sont arrivées, je suis encore confus. Il ne semble pas encore clair quand exactement l'optimiseur est autorisé à évaluer les choses dans la clause select. Je suppose que je vais devoir aller find la norme SQL moi-même et voir si je peux donner un sens à cela.

Joe Celko, qui a aidé à écrire les premières normes SQL, a posté quelque chose de similaire à ceci plusieurs fois dans divers newsfroups USENET. (Je saute les clauses qui ne s'appliquent pas à votre instruction SELECT.) Il disait habituellement quelque chose comme "C'est comme ça que les instructions sont supposées agir comme si elles fonctionnaient". En d'autres termes, les implémentations SQL doivent se comporter exactement comme si elles avaient suivi ces étapes, sans être obligées de faire chacune de ces étapes.

  1. Construire une table de travail à partir de tous les constructors de table dans la clause FROM.
  2. Supprimez de la table de travail les lignes qui ne satisfont pas à la clause WHERE.
  3. Construisez les expressions dans la clause SELECT par rapport à la table de travail.

Donc, à la suite de cela, aucun dbms SQL ne devrait agir comme s'il évaluait des fonctions dans la clause SELECT avant qu'il ne se comporte comme s'il appliquait la clause WHERE.

Dans une publication récente, Joe étend les étapes pour inclure les CTE .

CJ Date et Hugh Darwen disent essentiellement la même chose au chapitre 11 («Expressions de table») de leur ouvrage A Guide to the SQL Standard . Ils notent également que ce chapitre correspond à la section "Query Specification" (sections?) Dans les normes SQL.

Vous pensez à quelque chose appelé plan d'exécution de requête. Il est basé sur les règles d'optimization des requêtes, les index, les buffers temporaty et les statistics de time d'exécution. Si vous utilisez SQL Managment Studio, vous avez une boîte à outils sur votre éditeur de requêtes, où vous pouvez voir le plan d'exécution estimé, il montre comment votre requête va changer pour gagner en vitesse. Donc, si vous utilisez simplement votre table Name et qu'elle est en memory tampon, le moteur peut d'abord essayer de sous-interroger vos données, puis le joindre à une autre table.