Динамический доступ к курсору по имени столбца - PullRequest
3 голосов
/ 11 февраля 2011

Могу ли я получить динамический доступ к столбцу курсора? Я имею в виду по имени? как то так:

declare
 v_cursor := select * from emp;
begin
 FOR reg IN v_cursor LOOP
   dbms_output.put_line(**reg['column_name_as_string']**);
 end loop;
end;

Я знаю, что жирный шрифт не PL / SQL, но я ищу что-то подобное и нигде не могу его найти.

Спасибо!

Ответы [ 3 ]

6 голосов
/ 11 февраля 2011

Вы можете использовать пакет DBMS_SQL для создания и доступа к курсорам с динамическими запросами.

Однако получить доступ к столбцу по имени не очень просто, поскольку пакет DBMS_SQL используетпозиционирования и в динамическом запросе мы можем не знать порядок столбцов перед выполнением.

Кроме того, в контексте этого вопроса выясняется, что мы можем не знать, какой столбец мы хотим отобразить во время компиляции, мы будем предполагать, что столбец, который мы хотим отобразить, указан в качестве параметра.

Мы можем использовать DBMS_SQL.describe_columns для анализа столбцов запроса SELECT после его анализапостроить динамическое отображение столбцов.Мы будем предполагать, что все столбцы могут быть преобразованы в VARCHAR2, поскольку мы хотим отобразить их с помощью DBMS_OUTPUT.

Вот пример:

SQL> CREATE OR REPLACE PROCEDURE display_query_column(p_query VARCHAR2,
  2                                                   p_column VARCHAR2) IS
  3     l_cursor            INTEGER;
  4     l_dummy             NUMBER;
  5     l_description_table dbms_sql.desc_tab3;
  6     TYPE column_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(32767);
  7     l_mapping_table column_map_type;
  8     l_column_value  VARCHAR2(4000);
  9  BEGIN
 10     l_cursor := dbms_sql.open_cursor;
 11     dbms_sql.parse(l_cursor, p_query, dbms_sql.native);
 12     -- we build the column mapping
 13     dbms_sql.describe_columns3(l_cursor, l_dummy, l_description_table);
 14     FOR i IN 1 .. l_description_table.count LOOP
 15        l_mapping_table(l_description_table(i).col_name) := i;
 16        dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
 17     END LOOP;
 18     -- main execution loop
 19     l_dummy := dbms_sql.execute(l_cursor);
 20     LOOP
 21        EXIT WHEN dbms_sql.fetch_rows(l_cursor) <= 0;
 22        dbms_sql.column_value(l_cursor, l_mapping_table(p_column), l_column_value);
 23        dbms_output.put_line(l_column_value);
 24     END LOOP;
 25     dbms_sql.close_cursor(l_cursor);
 26  END;
 27  /

Procedure created

Мы можем вызвать эту процедуру с запросом, известным только во время выполнения:

SQL> set serveroutput on
SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'ENAME');
SMITH
ALLEN
WARD
JONES

PL/SQL procedure successfully completed

SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'EMPNO');
7369
7499
7521
7566

PL/SQL procedure successfully completed

Будьте осторожны с динамическим SQL: itимеет те же привилегии, что и пользователь, и поэтому может выполнять любые операторы DML и DDL , разрешенные для этой схемы.

Например, описанная выше процедура может использоваться для создания или удаления таблицы:

SQL> exec display_query_column('CREATE TABLE foo(id number)', '');
begin display_query_column('CREATE TABLE foo(id number)', ''); end;
ORA-01003: aucune instruction analysée
ORA-06512: à "SYS.DBMS_SQL", ligne 1998
ORA-06512: à "APPS.DISPLAY_QUERY_COLUMN", ligne 13
ORA-06512: à ligne 1

SQL> desc foo
Name Type   Nullable Default Comments 
---- ------ -------- ------- -------- 
ID   NUMBER Y      
1 голос
/ 14 февраля 2011

Вы имеете в виду что-то вроде:

declare
  cursor sel_cur is
  select * from someTable;

begin
  for rec in sel_cur
  loop
    dbms_output.put_line('col1: ' || rec.col1);
  end loop;
end;
1 голос
/ 11 февраля 2011

Вероятно, проще всего сделать запрос динамическим, если вы можете.

DECLARE
  v_cursor  SYS_REFCURSOR;
  dynamic_column_name VARCHAR2(30) := 'DUMMY';
  column_value  VARCHAR2(32767);
BEGIN
  OPEN v_cursor FOR 'SELECT ' || dynamic_column_name || ' FROM dual';
  LOOP
    FETCH v_cursor INTO column_value;
    EXIT WHEN v_cursor%NOTFOUND;
    dbms_output.put_line( column_value );
  END LOOP;
  CLOSE v_cursor;
END;

Если вы действительно хотите иметь жестко закодированный SELECT * и динамически выбирать столбец из этого по имени, я думаю, вы могли бы сделатьчто использование DBMS_SQL, как предполагает Винсент, будет несколько сложнее.

...