Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Lewati SELECT STATEMENT sebagai parameter IN ke prosedur dan jalankan di Oracle

Karena Anda tidak mengetahui kolom yang akan dikembalikan oleh kueri yang diteruskan pada waktu kompilasi, Anda tidak dapat merujuknya ke dalam loop secara statis.

Anda dapat menggunakan dbms_sql paket untuk melakukan ini secara dinamis:

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

Itu, pada dasarnya, mem-parsing pernyataan yang diteruskan, mengeksekusinya, mengambil setiap baris, mendapatkan setiap nilai kolom secara bergantian (sebagai string, yang dapat/harus diperluas untuk menghindari konversi implisit), dan menulis masing-masing dari mereka ke file pada gilirannya - menambahkan pembatas di antara mereka, dan baris baru terakhir setelah setiap baris.

Saat dipanggil dari blok anonim Anda yang membuat file yang berisi:

NLS_CHARACTERSET;AL32UTF8
NLS_RDBMS_VERSION;11.2.0.4.0

Sadarilah, thoguh, bahwa ini akan berjalan apa pun yang diberikannya, termasuk DDL (yang dijalankan saat diurai). Jika Anda tidak mengontrol bagaimana ini dipanggil, dan bahkan jika Anda melakukannya, Anda harus menambahkan validasi pernyataan yang diteruskan untuk memverifikasi bahwa itu sebenarnya hanya kueri.

Anda mungkin merasa lebih mudah untuk menjelajahi metode lain, seperti tabel eksternal (seperti yang disarankan @Kaushik), atau fungsionalitas klien.

Seperti yang disarankan @kfinity dalam komentar, Anda bisa menggunakan kursor ref untuk mengurai dan menjalankan kueri, yang seharusnya mencegah sesuatu yang buruk dijalankan. dbms_sql paket memiliki fungsi untuk mengubah kursor ref menjadi kursor asli , jadi gunakan insetad dari langkah-langkah buka, parsing, dan eksekusi eksplisit:

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

... yang menghasilkan file keluaran yang sama.

Kebetulan, jika Anda mau, Anda juga bisa menuliskan nama kolom sebagai baris header, sebelum loop 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Enkripsi Data Transparan Oracle akses tidak terenkripsi

  2. Bagaimana saya bisa membuat direktori pada sistem file?

  3. Tidak dapat memuat file atau perakitan Oracle.DataAccess

  4. Ubah port Oracle dari port 8080

  5. Mengurai tnsnames.ora dalam Visual C# 2008