Oracle
 sql >> Datenbank >  >> RDS >> Oracle

PL/SQL - Optionale Bedingungen in Where-Klausel - ohne dynamisches SQL?

Während Sie dies tun könnten ...

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

... die Leistung mit dynamischem SQL normalerweise besser ist , da dadurch ein gezielterer Abfrageplan generiert wird. In der obigen Abfrage kann Oracle nicht sagen, ob ein Index auf bcode oder lb oder type oder edate verwendet werden soll, und wird wahrscheinlich jedes Mal einen vollständigen Tabellenscan durchführen.

Natürlich müssen Sie Verwenden Sie Bind-Variablen in Ihrer dynamischen Abfrage, verketten Sie die Literalwerte nicht in die Zeichenfolge, da sonst die Leistung (und Skalierbarkeit und Sicherheit) sehr schlecht wird .

Um es klar zu sagen, die dynamische Version, die ich im Sinn habe, würde so funktionieren:

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;

Das bedeutet, dass die Ergebnisabfrage wird "sargable" sein (ein neues Wort für mich, das muss ich zugeben!), da der resultierende Abfragelauf (zum Beispiel) so aussehen wird:

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

Ich akzeptiere jedoch, dass dies in diesem Beispiel bis zu 16 harte Parses erfordern könnte. Die Klauseln „and :bv is null“ sind erforderlich, wenn natives dynamisches SQL verwendet wird, könnten aber durch die Verwendung von DBMS_SQL vermieden werden.

Hinweis:Die Verwendung von (1=1 or :bindvar is null) when the bind variable is null wurde in einem Kommentar von Michal Pravda vorgeschlagen, da es dem Optimierer ermöglicht, die Klausel zu eliminieren.