Comment forcer SQL Server à traiter les clauses CONTAINS avant les clauses WHERE?

J'ai une requête SQL qui utilise à la fois les clauses WHERE standard et les clauses CONTAINS de l'index de text intégral. La requête est générée dynamicment à partir du code et inclut un nombre variable de clauses WHERE et CONTAINS.

Pour que la requête soit rapide, il est très important que l'index de text intégral soit recherché avant que le rest des critères ne soit appliqué.

Toutefois, SQL Server choisit de traiter les clauses WHERE avant les clauses CONTAINS et cela provoque des parsings de tables et la requête est très lente.

Je suis capable de réécrire cela en utilisant deux requêtes et une table temporaire. Quand je le fais, la requête s'exécute 10 fois plus vite. Mais je ne veux pas faire cela dans le code qui crée la requête parce que c'est trop complexe.

Existe-t-il un moyen de forcer SQL Server à traiter le CONTAINS avant toute autre chose? Je ne peux pas forcer un plan (USE PLAN) car la requête est construite dynamicment et varie beaucoup.

Remarque: j'ai le même problème sur SQL Server 2005 et SQL Server 2008.

Vous pouvez signaler votre intention à l'optimiseur comme ceci

 SELECT * FROM ( SELECT * FROM WHERE CONTAINS ) T1 WHERE (normal conditions) 

Cependant, SQL est déclaratif: vous dites ce que vous voulez, pas comment le faire. L'optimiseur peut donc décider d'ignorer l'imbrication ci-dessus.

Vous pouvez forcer la table dérivée avec CONTAINS à être matérialisé avant l'application de la clause WHERE classique. Je ne garantirai pas la performance.

 SELECT * FROM ( SELECT TOP 2000000000 * FROM .... WHERE CONTAINS ORDER BY SomeID ) T1 WHERE (normal conditions) 

Essayez de le faire avec 2 requêtes sans tables temporaires:

 SELECT * FROM table WHERE id IN ( SELECT id FROM table WHERE contains_criterias ) AND further_where_classs 

Comme je l'ai noté ci-dessus, ce n'est pas aussi propre à «matérialiser» la table dérivée que la clause TOP que @gbn a proposée, mais un indicateur de jointure en boucle force un ordre d'évaluation et a fonctionné pour moi dans le passé avec deux tables différentes impliquées). Il y a cependant quelques problèmes:

  • La requête est moche
  • vous n'obtenez toujours aucune garantie que les autres parameters WHERE ne sont pas évalués qu'après la jointure (je serai intéressé de voir ce que vous obtenez)

Ici, il est cependant, étant donné que vous avez demandé:

 SELECT OriginalTable.XXX FROM ( SELECT XXX FROM OriginalTable WHERE CONTAINS XXX ) AS ContainsCheck INNER LOOP JOIN OriginalTable ON ContainsCheck.PrimaryKeyColumns = OriginalTable.PrimaryKeyColumns AND OriginalTable.OtherWhereConditions = OtherValues