sql >> Databasteknik >  >> RDS >> Oracle

Skickar dynamiska indataparametrar till "exekvera omedelbart"

Du kan inte tillhandahålla en stränglista med bindningsvärden som en using parameter, så det enda sättet jag kan se för att göra detta är med kapslade dynamiska SQL-anrop, vilket är lite rörigt och innebär att jag måste deklarera (och binda) alla möjliga parametrar i den inre. kapslad, dynamisk sats.

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;
/

Jag har använt den alternativa citeringsmekanismen här för att minska förvirring från undkomna enstaka citattecken. Det finns två kapslade nivåer av citat - den yttre avgränsas av q'[...]' och den inre avgränsad av q'^...^' , men du kan använda andra tecken om de är ett problem på grund av ditt faktiska tabellinnehåll. Att fly dessa citat på två nivåer skulle vara ganska fult och svårt att följa/få rätt; och du skulle också behöva oroa dig för ytterligare undvikande citat i ditt condition strängar, vilket redan skulle vara ett problem med din befintliga kod för det andra exemplet du angav eftersom det innehåller en bokstavlig text.

Med dina två exempeltabellrader och dummy-datum/dagvärdena visade jag ovanför resultatet från körning, det vill säga:

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

Det första att notera i den genererade satsen är declare-sektionen, som måste lista alla möjliga variabelnamn du kan ha i input_params , och ställ in dem från nya bindningsvariabler. Du måste känna till dessa redan i huvudblocket/proceduren, antingen som lokala variabler eller mer troliga procedurargument; men de måste alla dupliceras här, eftersom du vid det här laget inte vet vad som kommer att behövas.

Sedan har den satsen sin egen inre dynamiska SQL som i huvudsak är vad du ursprungligen gjorde, men sammanfogas i input_params sträng samt condition .

Den viktiga delen här är citatet. I den första, till exempel, båda :p_end_date och :p_start_date finns inom den andra nivån av citattecken, inom q'^...^' , så de är bundna till den inre dynamiska SQL, med värden från den lokala v_end_date och v_start_date från den inre execute immediate .

Hela det genererade blocket exekveras med bindningsvärden för alla möjliga variabelnamn, som ger värden för de lokala variablerna (via v_start_date date := :v_start_date; etc.) samtidigt som datatyperna bevaras; plus utdataflaggan.

Det blocket kör sedan sin interna execute immediate sats som endast använder de relevanta lokala variablerna, som nu har bundna värden; och utgångsflaggan som fortfarande är en bindningsvariabel från den yttre execute immediate , så att det yttre blocket fortfarande kan se sitt resultat.

Du kan se att den andra genererade satsen använder ett annat villkor och binder variabler och värden till det första, och flaggan utvärderas baserat på relevanta villkor och parametrar i varje fall.

För övrigt kan du ta bort dubblettreferensen till :o_flag (vilket inte är ett problem men jag tycker är lite förvirrande) genom att använda ett kasusuttryck istället:

    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. Postgresql fjärråtkomst ingen pg_hba.conf-post för värd

  2. Varför detta databasmigreringsfel efter att jag uppgraderat min version django-mptt?

  3. Hur man optimerar MySQL-prestanda med MySQLTuner

  4. Hur botar jag orsaken till viloläge undantag IllegalArgumentException inträffade när setter anropades?