Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

PL/SQL - Condições opcionais na cláusula where - sem sql dinâmico?


Enquanto você poderia fazer isso...
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

... o desempenho usando SQL dinâmico geralmente será melhor , pois gerará um plano de consulta mais direcionado. Na consulta acima, o Oracle não pode dizer se deve usar um índice em bcode ou lb ou type ou edate, e provavelmente executará uma varredura completa da tabela todas as vezes.

Claro, você deve use variáveis ​​de vinculação em sua consulta dinâmica, não concatene os valores literais na string, caso contrário, o desempenho (e escalabilidade e segurança) será muito ruim .

Para ser claro, a versão dinâmica que tenho em mente funcionaria assim:
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;

Isso significa que a consulta de resultado será ser "sargable" (uma palavra nova para mim, devo admitir!), já que a execução da consulta resultante será (por exemplo):
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

No entanto, aceito que isso possa exigir até 16 análises difíceis neste exemplo. As cláusulas "and :bv is null" são necessárias ao usar SQL dinâmico nativo, mas podem ser evitadas usando DBMS_SQL.

Nota:o uso de (1=1 or :bindvar is null) quando a variável de ligação é nula foi sugerido em um comentário de Michal Pravda, pois permite que o otimizador elimine a cláusula.