Oracle
 sql >> Base de données >  >> RDS >> Oracle

PL/SQL - Conditions facultatives dans la clause where - sans sql dynamique ?

Alors que vous pourriez le faire...

select num
from (select distinct q.num
       from cqqv q
       where 1=1
             and (:bcode is null or q.bcode = :bcode)
             and (:lb is null or q.lb = :lb)
             and (:type is null or q.type = :type)
             and (:edate is null or q.edate > :edate - 30)
       order by dbms_random.value()) subq
where rownum <= :numrows

... les performances avec SQL dynamique seront généralement meilleures , car cela générera un plan de requête plus ciblé. Dans la requête ci-dessus, Oracle ne peut pas dire s'il faut utiliser un index sur bcode ou lb ou type ou edate, et effectuera probablement une analyse complète de la table à chaque fois.

Bien sûr, vous devez utilisez des variables de liaison dans votre requête dynamique, ne concaténez pas les valeurs littérales dans la chaîne, sinon les performances (et l'évolutivité et la sécurité) seront très mauvaises .

Pour être clair, la version dynamique que j'ai en tête fonctionnerait comme ceci :

declare
    rc sys_refcursor;
    q long;
begin
    q := 'select num
    from (select distinct q.num
           from cqqv q
           where 1=1';

    if p_bcode is not null then
        q := q || 'and q.bcode = :bcode';
    else
        q := q || 'and (1=1 or :bcode is null)';
    end if;

    if p_lb is not null then
        q := q || 'and q.lb = :lb';
    else
        q := q || 'and (1=1 or :lb is null)';
    end if;

    if p_type is not null then
        q := q || 'and q.type = :type';
    else
        q := q || 'and (1=1 or :type is null)';
    end if;

    if p_edate is not null then
        q := q || 'and q.edate = :edate';
    else
        q := q || 'and (1=1 or :edate is null)';
    end if;

    q := q || ' order by dbms_random.value()) subq
    where rownum <= :numrows';

    open rc for q using p_bcode, p_lb, p_type, p_edate, p_numrows;
    return rc;
end;

Cela signifie que la requête de résultat sera être "sargable" (un nouveau mot pour moi, je dois l'admettre !) puisque l'exécution de la requête résultante sera (par exemple) :

select num
from (select distinct q.num
       from cqqv q
       where 1=1
             and q.bcode = :bcode
             and q.lb = :lb
             and (1=1 or :type is null)
             and (1=1 or :edate is null)
       order by dbms_random.value()) subq
where rownum <= :numrows

Cependant, j'accepte que cela puisse nécessiter jusqu'à 16 analyses difficiles dans cet exemple. Les clauses "and :bv is null" sont obligatoires lors de l'utilisation de SQL dynamique natif, mais peuvent être évitées en utilisant DBMS_SQL.

Remarque :l'utilisation de (1=1 or :bindvar is null) lorsque la variable de liaison est nulle a été suggéré dans un commentaire de Michal Pravda, car cela permet à l'optimiseur d'éliminer la clause.