Oracle
 sql >> Database >  >> RDS >> Oracle

PL/SQL - Condizioni facoltative nella clausola where - senza sql dinamico?

Mentre potresti farlo...

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

... le prestazioni utilizzando l'SQL dinamico saranno generalmente migliori , poiché genererà un piano di query più mirato. Nella query precedente, Oracle non è in grado di dire se utilizzare un indice su bcode o lb o type o edate e probabilmente eseguirà una scansione completa della tabella ogni volta.

Naturalmente, devi usa le variabili bind nella tua query dinamica, non concatena i valori letterali nella stringa, altrimenti le prestazioni (e la scalabilità e la sicurezza) saranno pessime .

Per essere chiari, la versione dinamica che ho in mente funzionerebbe in questo modo:

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;

Ciò significa che la query del risultato sarà essere "sargable" (una nuova parola per me devo ammettere!) poiché l'esecuzione della query risultante sarà (ad esempio):

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

Tuttavia, accetto che ciò potrebbe richiedere fino a 16 analisi hard in questo esempio. Le clausole "and :bv is null" sono richieste quando si utilizza l'SQL dinamico nativo, ma possono essere evitate utilizzando DBMS_SQL.

Nota:l'uso di (1=1 or :bindvar is null) quando la variabile bind è nulla è stato suggerito in un commento da Michal Pravda, in quanto consente all'ottimizzatore di eliminare la clausola.