Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Przekazywanie dynamicznych parametrów wejściowych do „wykonaj natychmiast”

Nie możesz podać listy ciągów wartości wiązania jako using parametr, więc jedynym sposobem, w jaki mogę to zrobić, są zagnieżdżone dynamiczne wywołania SQL, co jest nieco niechlujne i oznacza konieczność zadeklarowania (i powiązania) wszystkich możliwych parametrów w wewnętrznym. zagnieżdżona, dynamiczna instrukcja.

declare
  v_execute_statement varchar2(4000);
  v_flag varchar2(1);
  v_start_date date := date '2018-01-01';
  v_end_date date := date '2018-01-31';
  v_joining_day varchar2(9) := 'MONDAY';
begin
  -- loop over all rows for demo
  for rec in (
    select condition, input_params
    From your_table
  )
  loop
    v_execute_statement := q'[
      DECLARE
        v_start_date date := :v_start_date;
        v_end_date date := :v_end_date;
        v_joining_day varchar2(9) := :v_joining_day;
      BEGIN 
        EXECUTE IMMEDIATE q'^
          BEGIN
            IF ]' || rec.condition || q'[ THEN
              :o_flag := 'Y';
            ELSE
              :o_flag := 'N';
            END IF;
          END;^'
        USING ]' || rec.input_params || q'[, OUT :v_flag;
      END;]';

    dbms_output.put_line('Statement: ' || v_execute_statement);

    EXECUTE IMMEDIATE v_execute_statement
    USING v_start_date, v_end_date, v_joining_day, OUT v_flag;

    dbms_output.put_line('Result flag: ' || v_flag);
  end loop;
end;
/

Użyłem alternatywny mechanizm cytowania tutaj, aby zmniejszyć zamieszanie związane z pojedynczymi cudzysłowami. Istnieją dwa zagnieżdżone poziomy cytowania - zewnętrzny ograniczony przez q'[...]' a wewnętrzny oddzielony przez q'^...^' , ale możesz użyć innych znaków, jeśli stanowią one problem ze względu na rzeczywistą zawartość tabeli. Ucieczka od tych cytatów na dwa poziomy byłaby dość brzydka i trudna do naśladowania/dokładnia; i musiałbyś się również martwić o dalsze unikanie cudzysłowów w swoim condition ciągi, co już stanowiłoby problem z istniejącym kodem dla drugiej dostarczonej próbki, ponieważ zawiera ona w sobie literał tekstowy.

Z dwoma przykładowymi wierszami tabeli i fikcyjnymi wartościami daty/dni, które pokazałem powyżej danych wyjściowych z uruchomienia, to:

Statement: 
      DECLARE
        v_start_date date := :v_start_date;
        v_end_date date := :v_end_date;
        v_joining_day varchar2(9) := :v_joining_day;
      BEGIN 
        EXECUTE IMMEDIATE q'^
          BEGIN
            IF :p_end_date < :p_start_date THEN
              :o_flag := 'Y';
            ELSE
              :o_flag := 'N';
            END IF;
          END;^'
        USING v_end_date, IN v_start_date, OUT :o_flag;
      END;
Result flag: N
Statement: 
      DECLARE
        v_start_date date := :v_start_date;
        v_end_date date := :v_end_date;
        v_joining_day varchar2(9) := :v_joining_day;
      BEGIN 
        EXECUTE IMMEDIATE q'^
          BEGIN
            IF :p_joining_day = 'MONDAY' THEN
              :o_flag := 'Y';
            ELSE
              :o_flag := 'N';
            END IF;
          END;^'
        USING v_joining_day, OUT :o_flag;
      END;
Result flag: Y

Pierwszą rzeczą, na którą należy zwrócić uwagę w wygenerowanej instrukcji, jest sekcja define , która musi zawierać listę wszystkich możliwych nazw zmiennych, które możesz mieć w input_params i ustaw je z nowych zmiennych wiązania. Musisz je znać już w głównym bloku/procedurze, jako zmienne lokalne lub bardziej prawdopodobne argumenty procedury; ale wszystkie muszą być tutaj zduplikowane, ponieważ w tym momencie nie wiesz, co będzie potrzebne.

Następnie ta instrukcja ma swój własny wewnętrzny dynamiczny SQL, który jest zasadniczo tym, co robiłeś pierwotnie, ale łączy się w input_params string oraz condition .

Ważną częścią jest tutaj cytowanie. W pierwszym, na przykład, oba :p_end_date i :p_start_date znajdują się na drugim poziomie cudzysłowów, w obrębie q'^...^' , więc są powiązane z wewnętrznym dynamicznym kodem SQL, z wartościami z lokalnego v_end_date i v_start_date z tego wewnętrznego execute immediate .

Cały wygenerowany blok jest wykonywany z wartościami wiązania dla wszystkich możliwych nazw zmiennych, które dostarczają wartości dla zmiennych lokalnych (poprzez v_start_date date := :v_start_date; itp.) z zachowaniem typów danych; plus flaga wyjścia.

Ten blok następnie wykonuje swoje wewnętrzne execute immediate instrukcja używająca tylko odpowiednich zmiennych lokalnych, które mają teraz powiązane wartości; i flaga wyjściowa, która nadal jest zmienną bind z zewnętrznego execute immediate , aby zewnętrzny blok nadal mógł zobaczyć swój wynik.

Widać, że druga wygenerowana instrukcja używa innego warunku i wiąże zmienne i wartości z pierwszym, a flaga jest oceniana w każdym przypadku na podstawie odpowiedniego warunku i parametrów.

Nawiasem mówiąc, możesz usunąć zduplikowane odniesienie do :o_flag (co nie stanowi problemu, ale wydaje mi się nieco mylące), używając zamiast tego wyrażenia przypadku:

    v_execute_statement := q'[
      DECLARE
        v_start_date date := :v_start_date;
        v_end_date date := :v_end_date;
        v_joining_day varchar2(9) := :v_joining_day;
      BEGIN 
        EXECUTE IMMEDIATE q'^
          BEGIN
            :o_flag := CASE WHEN ]' || rec.condition || q'[ THEN 'Y' ELSE 'N' END;
          END;^'
        USING OUT :v_flag, ]' || rec.input_params || q'[;
      END;]';



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Deklarowanie i ustawianie zmiennych w instrukcji Select

  2. potrzebujesz pomocy w optymalizacji zapytania Oracle

  3. Firmy programistyczne, które pracują nad Oracle D2k, PLSQL Technologies w Noida

  4. Wykonywanie wielu instrukcji SQL za pomocą NHibernate

  5. Oracle PL/SQL Release 12.2.0.1.0 vs 12.1.0.2.0 - wykonanie natychmiastowe z parametrami