2010-05-20 4 views
3

J'ai une table foo et une barre de table, où chaque foo pourrait avoir une barre (et une barre pourrait appartenir à plusieurs foos). Maintenant, j'ai besoin de sélectionner tous les foos avec une barre. Mon sql ressemble à ceciLes sous-requêtes conditionnelles sont-elles optimisées si la condition est fausse?

SELECT * 
    FROM foo f 
WHERE [...] 
    AND ($param IS NULL OR (SELECT ((COUNT(*))>0) 
          FROM bar b 
          WHERE f.bar = b.id)) 

avec $ param étant remplacé lors de l'exécution.

La question est: La sous-requête sera-t-elle exécutée même si param est nul, ou les dbms optimiseront-ils la sous-requête?

Nous utilisons mysql, mssql et oracle. Y at-il une différence entre ceux-ci en ce qui concerne ce qui précède?

Répondre

2

Cela dépend. Si vous transmettez cette requête au SGBD à chaque fois, le compilateur doit réaliser qu'il n'a pas besoin d'appeler la sous-requête. S'il s'agit d'une procédure statique, cela dépend de la façon dont le compilateur stocke le plan d'exécution. Si elle stocke le plan d'exécution la première fois que la procédure est appelée et que la première fois qu'elle est appelée $ param n'est pas nulle, elle peut en fait appeler la sous-requête chaque fois que la procédure est exécutée.

Sur une note différente, vous devriez envisager Existant à la place du comte (*) pour ce type de contrôle

Select .. 
From foo f 
Where .... 
    And ($param Is Null 
      Or Exists (
         Select 1 
         From bar b 
         Where b.id = f.bar 
         )) 
1

Dans ce cas, je vous recommande de procéder à l'optimisation vous-même dans le code de l'application, plutôt que de compter sur l'optimiseur de 3 SGBDR différents pour gérer cela comme vous le souhaitez.

Si $ param est nul, sélectionnez simplement dans la table foo. Sinon, rejoignez la table à barres.

pseudo-code:

if ($param is null) 
    SELECT * 
    FROM foo f 
    WHERE [...] 
else 
    SELECT distinct f.* 
    FROM foo f 
    inner join bar b on f.bar = b.id 
    WHERE [...] 
end if 
+0

Je ne recommande pas cette approche, pour l'amour que vous serez en encapsulant trois (ou plus) syntaxe de bases de données qui va diverger. IE: MySQL supporte LIMIT, ce que ne font ni SQL Server ni Oracle - SQL Server utilise TOP alors qu'Oracle utilise ROWNUM. Utilisez une technologie d'abstraction de base de données telle que Hibernate/etc pour ne pas avoir à coder vous-même les différents SQL, ou utilisez la procédure/fonction stockée de chaque base de données pour coder et régler les requêtes pour chacun. –

+0

Pour clarifier, mon pseudo-code 'if ... else ... end if' est censé être dans la couche application, pas en SQL. Je ne recommande pas d'ajouter de la complexité au SQL, en fait je pense que le SQL devrait être simplifié. Le point principal que je faisais est que si vous avez un simple conditionnel comme '$ param is null', vous feriez probablement mieux de le retirer du SQL et dans la couche application. Cela devrait fonctionner indépendamment du fait que OP utilise Hibernate, etc ou écrit directement SQL. –

+0

Oui, nous utilisons une couche d'abstraction db, fabriquée à la maison, avec des requêtes nommées, qui sont écrites dans des fichiers xml et accessibles depuis notre code. Les paramètres sont transmis à partir du code et le sql est généré par la couche d'abstraction. Par conséquent, le moyen le plus simple pour l'instant est d'utiliser le type de ($ param est null || $ param == ...) comme je l'ai fait dans l'exemple. Le seul inconvénient à ce jour a été que le sql résultant est encombré avec null est null ... qui empêche le débogage un peu, mais n'a jamais été un problème de performance jusqu'à présent - jusqu'à présent, où j'ai besoin d'une jointure conditionnelle. –