sql >> Base de Datos >  >> RDS >> Oracle

PL/SQL - Condiciones opcionales en la cláusula where - ¿sin sql dinámico?

Si bien podrías hacer esto...

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

... el rendimiento con SQL dinámico normalmente será mejor , ya que generará un plan de consulta más específico. En la consulta anterior, Oracle no puede saber si usar un índice en bcode o lb o type o edate, y probablemente realizará un escaneo completo de la tabla cada vez.

Por supuesto, debes use variables de vinculación en su consulta dinámica, no concatene los valores literales en la cadena; de lo contrario, el rendimiento (y la escalabilidad y la seguridad) será muy malo .

Para que quede claro, la versión dinámica que tengo en mente funcionaría así:

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;

Esto significa que la consulta de resultado será ser "sargable" (¡una palabra nueva para mí, debo admitirlo!) ya que la ejecución de la consulta resultante será (por ejemplo):

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

Sin embargo, acepto que esto podría requerir hasta 16 análisis duros en este ejemplo. Las cláusulas "y :bv es nulo" son necesarias cuando se usa SQL dinámico nativo, pero se pueden evitar usando DBMS_SQL.

Nota:el uso de (1=1 or :bindvar is null) cuando la variable de vinculación es nula se sugirió en un comentario de Michal Pravda, ya que permite que el optimizador elimine la cláusula.