通过列名动态访问游标

发布于 2024-10-16 21:12:29 字数 273 浏览 2 评论 0原文

我可以动态访问游标的列吗?我的意思是名字?像这样的东西:

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,但我正在寻找类似的东西,但在任何地方都找不到它。

Can I access a cursor's column dynamically? I mean by name? something like this:

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;

I know the bold part is not PL/SQL, but I'm looking for something like that and can't find it anywhere.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

听不够的曲调 2024-10-23 21:12:29

您可以使用包 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 时要小心:它具有与用户相同的权限,因此可以执行允许的任何 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      

You can use the package DBMS_SQL to create and access cursors with dynamic queries.

However it's not really straightforward to access a column by name because the DBMS_SQL package uses positioning and in a dynamic query we may not know the order of the columns before the execution.

Furthermore, in the context of this question, it appears that we may not know which column we want to display at compile time, we will assume that the column we want to display is given as a parameter.

We can use DBMS_SQL.describe_columns to analyze the columns of a SELECT query after it has been parsed to build a dynamic mapping of the columns. We will assume that all columns can be cast into VARCHAR2 since we want to display them with DBMS_OUTPUT.

Here's an example:

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

We can call this procedure with a query known only at run-time:

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

Use caution with dynamic SQL: it has the same privileges as the user and can therefore execute any DML and DDL statement allowed for this schema.

For instance, the above procedure could be used to create or drop a table:

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      
冷默言语 2024-10-23 21:12:29

如果可以的话,使查询动态化可能是最简单的。

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 * 并按名称动态选择列,我认为您可以按照 Vincent 的建议使用 DBMS_SQL 来实现,但它会更复杂一些。

It's probably easiest to make the query dynamic if you can.

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;

If you really want to have a hardcoded SELECT * and dynamically select a column from that by name, I think you could do that using DBMS_SQL as Vincent suggests, but it will be somewhat more complex.

白馒头 2024-10-23 21:12:29

你的意思是这样的:

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;

You mean something like:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文