sql >> データベース >  >> RDS >> Oracle

SELECT STATEMENTをINパラメータとしてプロシージャに渡し、Oracleで実行します

    コンパイル時に渡されたクエリによって返される列がわからないため、ループ内で静的に参照することはできません。

    dbms_sqlを使用できます これを動的に行うパッケージ:

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

    つまり、基本的に、渡されたステートメントを解析して実行し、各行をフェッチして、各列の値を順番に取得し(文字列として、暗黙の変換を回避するために展開する必要があります)、それらのそれぞれをファイルに書き込みます。次に、それらの間に区切り文字を追加し、各行の後に最後の改行を追加します。

    以下を含むファイルを作成する匿名ブロックから呼び出された場合:

    NLS_CHARACTERSET;AL32UTF8
    NLS_RDBMS_VERSION;11.2.0.4.0
    

    これは、DDL(解析時に実行される)を含め、指定されたものであれば何でも実行されることに注意してください。これがどのように呼び出されるかを制御しない場合、実際に制御する場合でも、渡されたステートメントの検証を追加して、それが実際には単なるクエリであることを確認する必要があります。

    外部テーブル(@Kaushikが提案したように)やクライアント機能など、他の方法を検討する方が簡単な場合があります。

    @kfinityがコメントで示唆しているように、refカーソルを使用してクエリを解析および実行できます。これにより、厄介なものが実行されるのを防ぐことができます。 dbms_sql パッケージ参照カーソルをネイティブカーソルに変換する機能があります 、したがって、明示的なオープン、解析、および実行のステップの挿入図を使用します。

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

    ...同じ出力ファイルを生成します。

    ちなみに、必要に応じて、fetch-rowsループの前に、列名をヘッダー行として書き出すこともできます。

       -- 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. PHPとMySQLを使用したサーバーサイドイベント

    2. OracleでのIPv4/IPv6アドレスの表現

    3. ROR4でMySQLストアドプロシージャを呼び出す

    4. 除外制約`EXCLUDEUSING gist(c WITH &&)`はどういう意味ですか?