sql >> Database >  >> RDS >> Oracle

Dynamische invoerparameters doorgeven om 'onmiddellijk uit te voeren'

U kunt geen stringlijst met bindwaarden als een using parameter, dus de enige manier die ik kan zien om dit te doen, is met geneste dynamische SQL-aanroepen, wat een beetje rommelig is, en betekent dat ik alle mogelijke parameters in de binnenste moet declareren (en binden). geneste, dynamische verklaring.

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

Ik heb het alternatieve citatiemechanisme hier om verwarring door ontsnapte enkele aanhalingstekens te verminderen. Er zijn twee geneste niveaus van aanhalingstekens - de buitenste begrensd door q'[...]' en de binnenste begrensd door q'^...^' , maar u kunt andere tekens gebruiken als dat een probleem is vanwege uw werkelijke tabelinhoud. Ontsnappen aan die aanhalingstekens voor twee niveaus zou behoorlijk lelijk zijn en moeilijk te volgen / goed te krijgen; en je zou je ook zorgen moeten maken over het ontsnappen van aanhalingstekens in je condition strings, wat al een probleem zou zijn met je bestaande code voor het tweede voorbeeld dat je hebt opgegeven, omdat dat een letterlijke tekst bevat.

Met je twee voorbeeldtabelrijen en de dummy-datum / dag-waarden die ik boven de uitvoer van hardlopen liet zien, namelijk:

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

Het eerste dat opvalt in de gegenereerde instructie is de declare-sectie, die alle mogelijke variabelenamen moet vermelden die je zou kunnen hebben in input_params en stel ze in vanuit nieuwe bindvariabelen. U moet deze al in het hoofdblok/de hoofdprocedure kennen, hetzij als lokale variabelen, hetzij als meer waarschijnlijke procedure-argumenten; maar ze moeten hier allemaal worden gedupliceerd, omdat je op dit moment niet weet welke je nodig hebt.

Dan heeft die instructie zijn eigen innerlijke dynamische SQL, wat in wezen is wat u oorspronkelijk deed, maar samenvoegt in de input_params tekenreeks en condition .

Het belangrijkste hier is het citeren. In de eerste bijvoorbeeld, zowel :p_end_date en :p_start_date bevinden zich binnen het tweede niveau van aanhalingstekens, binnen de q'^...^' , dus ze zijn gebonden aan de innerlijke dynamische SQL, met waarden van de lokale v_end_date en v_start_date vanuit die innerlijke execute immediate .

Dat hele gegenereerde blok wordt uitgevoerd met bindwaarden voor alle mogelijke variabelenamen, die waarden leveren voor de lokale variabelen (via v_start_date date := :v_start_date; enz.) met behoud van gegevenstypen; plus de uitvoervlag.

Dat blok voert dan zijn interne execute immediate statement met alleen de relevante lokale variabelen, die nu gebonden waarden hebben; en de uitvoervlag die nog steeds een bindvariabele is van de buitenste execute immediate , zodat het buitenste blok het resultaat nog steeds kan zien.

U kunt zien dat de tweede gegenereerde instructie een andere voorwaarde gebruikt en variabelen en waarden aan de eerste bindt, en de vlag wordt in elk geval geëvalueerd op basis van de relevante voorwaarde en parameters.

Overigens zou je de dubbele verwijzing naar :o_flag . kunnen verwijderen (wat geen probleem is, maar ik vind het een beetje verwarrend) door in plaats daarvan een hoofdletteruitdrukking te gebruiken:

    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. SQL gebruikt door komma's gescheiden waarden met IN-clausule

  2. Oracle - Maak alleen een index als deze niet bestaat

  3. PostgreSQL:database herstellen vanaf dump - syntaxisfout

  4. Hoe een zoekopdracht te formuleren om alle cursussen te tonen die door een persoon zijn gevolgd