Comment forcer SQL Server 2005 à exécuter une jointure avant le où?
-
10-07-2019 - |
Question
J'ai une requête SQL qui joint une table de tarification à une table contenant des réponses fournies par l'utilisateur. Ma requête est utilisée pour obtenir le prix en fonction de la quantité entrée. Ci-dessous ma déclaration SQL:
SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount FROM Price
INNER JOIN JobQuestion
ON Price.QuestionFK=JobQuestion.QuestionFK
AND JobQuestion.JobFK=1
WHERE Price.Min <= JobQuestion.Value
AND Price.Max >= JobQuestion.Value
Le problème est que SQL Server exécute la clause where avant le JOIN et renvoie l'erreur:
La conversion a échoué lors de la conversion du Valeur varchar 'TEST' pour le type de données int.
car il fait les comparaisons min et max avant la jointure ('TEST' est une valeur entrée par l'utilisateur valide dans la table JobQuestion, mais ne doit pas être renvoyée lorsque JobQuestion est joint à Price). Je pense que SQL Server choisit d’exécuter le WHERE car, pour une raison quelconque, l’analyseur pense que ce serait une requête plus efficace. Si je viens de courir
SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount FROM Price
INNER JOIN JobQuestion
ON Price.QuestionFK=JobQuestion.QuestionFK
AND JobQuestion.JobFK=1
Je récupère ces résultats:
500 1 500 272.00
500 501 1000 442.00
500 1001 2000 782.00
Ainsi, l'ajout du WHERE devrait filtrer les deux derniers et simplement renvoyer le premier enregistrement. Comment forcer SQL à exécuter en premier le JOIN ou à utiliser une autre technique pour filtrer uniquement les enregistrements dont j'ai besoin?
La solution
Tout d’abord, c’est très probablement un signe de mauvaise conception.
Si vous ne pouvez pas modifier le schéma, vous pourriez peut-être forcer ce comportement à l'aide des astuces Citation:
Les astuces sont des options ou des stratégies spécifiées pour être appliquées par le processeur de requêtes SQL Server sur les instructions SELECT, INSERT, UPDATE ou DELETE. Les astuces remplacent tout plan d'exécution que l'optimiseur de requête peut sélectionner pour une requête.
Et d'autres encore:
Attention:
Étant donné que l'optimiseur de requêtes SQL Server sélectionne généralement le meilleur plan d'exécution pour une requête, nous vous recommandons d'utiliser & Lt; join_hint > ;, < query_hint > ;, et < table_hint > être utilisé qu'en dernier recours par des développeurs expérimentés et des administrateurs de bases de données.
Autres conseils
Essayez & "; reformulez &"; la requête comme suit:
SELECT *
FROM (
SELECT JobQuestion.Value,
Price.Min,
Price.Max,
Price.Amount
FROM Price
INNER
JOIN JobQuestion
ON Price.QuestionFK = JobQuestion.QuestionFK
AND JobQuestion.JobFK = 1
) SQ
WHERE SQ.Min <= SQ.Value
AND SQ.Max >= SQ.Value
Selon la réponse de Christian Hayter, si vous avez le choix, changez le format de la table =)
Vous ne devriez pas comparer les chaînes à ints. Si vous avez une quelconque influence sur la conception de votre table, divisez les deux utilisations différentes de la JobQuestion.Value
colonne en deux colonnes différentes.
Au cas où vous n’auriez aucune influence sur la conception de votre table - Pourriez-vous essayer de filtrer ces enregistrements avec des valeurs numériques en utilisant ISNUMERIC ()? J'imagine que l'ajout de ceci à votre clause "où" pourrait vous aider.
Vous pouvez probablement supprimer les éléments where ... et simplement les ajouter en tant que prédicats à votre jointure. Comme il s’agit d’une jointure interne, cela devrait fonctionner
SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount
FROM Price
INNER JOIN JobQuestion
ON Price.QuestionFK=JobQuestion.QuestionFK
AND JobQuestion.JobFK=1
AND Price.Min <= JobQuestion.Value
AND Price.Max >= JobQuestion.Value
Vous pouvez utiliser TRY_PARSE sur les chaînes de chaînes à convertir en valeurs numériques. Si SQL ne peut pas être converti, vous obtiendrez NULL au lieu du message d'erreur.
P.S. Cette fonctionnalité est introduite pour la première fois dans SQL 2012, elle pourrait donc être utile.