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

Send SELECT STATEMENT som IN-parameter for at behandle og udføre i Oracle

Da du ikke kender de kolonner, der returneres af den indsendte forespørgsel på kompileringstidspunktet, kan du ikke statisk henvise til dem inde i løkken.

Du kan bruge dbms_sql pakke for at gøre dette dynamisk:

CREATE OR REPLACE PROCEDURE p_create_text_file (
   loc IN VARCHAR2
   , file IN VARCHAR2
   , select_statement in varchar2
   , line_statement in varchar2 -- not used?
)
IS
   fid UTL_FILE.FILE_TYPE := UTL_FILE.FOPEN (loc, file, 'W');

   -- for dbms_sql
   l_c pls_integer;
   l_col_cnt pls_integer;
   l_desc_t dbms_sql.desc_tab3;
   l_rc pls_integer;
   l_varchar varchar2(4000);
BEGIN
   -- create cursor and prepare from passed-in statement
   l_c := dbms_sql.open_cursor;
   dbms_sql.parse(c=>l_c, statement=>select_statement,
      language_flag=>dbms_sql.native);
   dbms_sql.describe_columns3(c => l_c, col_cnt => l_col_cnt,
      desc_t => l_desc_t);

   -- define all columns as strings; this will end up with implicit conversion
   -- of dates etc. using NLS settings, so shoudl be finsessed based on data
   -- actual data type really...
   for i in 1..l_col_cnt loop
      dbms_sql.define_column(c=>l_c, position=>i,
         column=>l_varchar, column_size=>4000);
   end loop;

   -- execute the query
   l_rc := dbms_sql.execute(c=>l_c);

   -- fetch each row in turn
   while dbms_sql.fetch_rows(c=>l_c) > 0 loop
      -- for each column from describe
      for i in 1..l_col_cnt loop
         -- get the column value for this row (again, as string...)
         dbms_sql.column_value(l_c, i, l_varchar);
         -- write out to file, with delimiter after first column
         if i > 1 then
            UTL_FILE.PUT (fid, ';');
         end if;
         UTL_FILE.PUT (fid, l_varchar);
      end loop;
      UTL_FILE.NEW_LINE (fid);
   end loop;

   dbms_sql.close_cursor(l_c);

   UTL_FILE.FCLOSE (fid);
EXCEPTION
    WHEN OTHERS THEN UTL_FILE.FCLOSE (fid);
END;
/

Det, dybest set, analyserer den indsendte sætning, udfører den, henter hver række, henter hver kolonneværdi efter tur (som en streng, der kunne/bør udvides for at undgå implicitte konverteringer), og skriver hver af dem ud til filen igen - tilføjelse af en afgrænsning mellem dem og en sidste ny linje efter hver række.

Når du kalder fra din anonyme blok, opretter det en fil, der indeholder:

NLS_CHARACTERSET;AL32UTF8
NLS_RDBMS_VERSION;11.2.0.4.0

Vær dog opmærksom på, at dette vil køre alt, hvad det er givet, inklusive DDL (som udføres, når det parses). Hvis du ikke kontrollerer, hvordan dette kaldes, og faktisk selvom du gør det, bør du tilføje validering af den indsendte erklæring for at bekræfte, at det faktisk kun er en forespørgsel.

Du finder det måske nemmere at udforske andre metoder, såsom eksterne tabeller (som @Kaushik foreslog) eller klientfunktionalitet.

Som @kfinity foreslog i en kommentar, kunne du bruge en ref-markør til at parse og udføre forespørgslen, hvilket skulle forhindre noget ubehageligt i at blive kørt. dbms_sql pakke har en funktion til at konvertere en ref-markør til en oprindelig markør , så brug denne indsættelse af de eksplicitte åbne, parse og udfør trin:

CREATE OR REPLACE PROCEDURE p_create_text_file (
   loc IN VARCHAR2
   , file IN VARCHAR2
   , select_statement in varchar2
   , line_statement in varchar2 -- not used?
)
IS
   fid UTL_FILE.FILE_TYPE := UTL_FILE.FOPEN (loc, file, 'W');

   -- for initial parse and execute
   l_refcursor sys_refcursor;

   -- for dbms_sql
   l_c pls_integer;
   l_col_cnt pls_integer;
   l_desc_t dbms_sql.desc_tab3;
   l_rc pls_integer;
   l_varchar varchar2(4000);
BEGIN
   -- open ref cursor for the statement
   open l_refcursor for select_statement;

   -- convert ref cursor to dbms_sql cursor
   l_c := dbms_sql.to_cursor_number(l_refcursor);
   dbms_sql.describe_columns3(c => l_c, col_cnt => l_col_cnt,
      desc_t => l_desc_t);

   -- define all columns as strings; this will end up with implicit conversion
   -- of dates etc. using NLS settings, so shoudl be finsessed based on data
   -- actual data type really...
   for i in 1..l_col_cnt loop
      dbms_sql.define_column(c=>l_c, position=>i,
         column=>l_varchar, column_size=>4000);
   end loop;

   -- fetch each row in turn
   while dbms_sql.fetch_rows(c=>l_c) > 0 loop
      -- for each column from describe
      for i in 1..l_col_cnt loop
         -- get the column value for this row (again, as string...)
         dbms_sql.column_value(l_c, i, l_varchar);
         -- write out to file, with delimiter after first column
         if i > 1 then
            UTL_FILE.PUT (fid, ';');
         end if;
         UTL_FILE.PUT (fid, l_varchar);
      end loop;
      UTL_FILE.NEW_LINE (fid);
   end loop;

   dbms_sql.close_cursor(l_c);

   UTL_FILE.FCLOSE (fid);
EXCEPTION
    WHEN OTHERS THEN UTL_FILE.FCLOSE (fid);
END;
/

... som producerer den samme outputfil.

Hvis du ville, kunne du i øvrigt også skrive kolonnenavnene ud som en overskriftsrække, før henterækkernes løkke:

   -- write column names as header row
   for i in 1..l_col_cnt loop
      if i > 1 then
         UTL_FILE.PUT (fid, ';');
      end if;
      UTL_FILE.PUT (fid, l_desc_t(i).col_name);
   end loop;
   UTL_FILE.NEW_LINE (fid);



  1. Indsæt et tidsstempel i databasen via ContentValues

  2. Hvordan lister man poster med dato fra de sidste 10 dage?

  3. Hvordan skriver man en MYSQL CASE WHEN-sætning med flere søgebetingelser?

  4. Oracle sql-typer over dblink