如何通过 PL/SQL 过程和表名作为变量打印动态 SQL 中的所有表行?

Posted

技术标签:

【中文标题】如何通过 PL/SQL 过程和表名作为变量打印动态 SQL 中的所有表行?【英文标题】:How to print all table rows in dynamic SQL via PL/SQL procedure and table name as a variable? 【发布时间】:2020-04-06 15:25:21 【问题描述】:

我正在尝试使用动态 SQL 和循环通过DBMS_OUTPUT.PUT_LINE 打印一个表,因为我的表名必须是一个变量,因为它每个月都会更改,下面是我的代码,

我也很困惑如何存储数据以及如何将其放入变量中?变量是否需要是 CURSOR?为了在下面的代码中进行测试,我正在使用HRschema 中的演示employee 表。

另外,还有比 varchar2(4000) 更大的数据类型,因为我需要将我的 SQL 语句存储到一个变量中以使用 execute immediately,并且该变量需要存储一个非常大的复杂 SQL 查询。

SET SERVEROUTPUT ON SIZE 1000000;
DECLARE
v_TBL_NAME varchar2(200):='EMPLOYEES';
v_sql varchar2(200):='IS select * from ' ||v_TBL_NAME;
CURSOR C1;
BEGIN
EXECUTE IMMEDIATE v_sql INTO C1;
FOR REC IN C1
LOOP
DBMS_OUTPUT.PUT_LINE(REC.EMPLOYEE_ID||' '||REC.FIRST_NAME);
END LOOP;
END;
/

【问题讨论】:

4000 是 SQL/DDL 中的大小限制(以后版本可以增加);但在 PL/SQL 中,无论如何限制是 32k。如果你需要比这更大,你可以使用 CLOB,但在这里可能有点过分了? 谢谢 Alex,打印台怎么样?我的表的问题是一个部分表,它的名称每个月都会更改billing202001@billingdb 下个月billing202002@billingdb,经过多次研究,我发现我必须使用动态 SQL,但我不知道如何将数据提取到变量中并在 for 循环中打印。 列名总是一样的吗? 列名始终相同,但表名每个月都在变化。 为什么每个月都创建不同的表?为什么不在一个 BILLING 表上创建一个分区? 【参考方案1】:

您可以使用引用游标并批量收集到嵌套表中。然后像这样遍历表格:

DECLARE
  TYPE lt_record IS RECORD
  (
    empID INTEGER,
    fname VARCHAR2(100)
  );
  TYPE lt_recordTable IS TABLE OF lt_record;
  l_cTableCursor SYS_REFCURSOR;
  l_sTableName VARCHAR2(1000) := 'TABLE NAME';
  l_tRecords lt_recordTable;
BEGIN
  OPEN l_cTableCursor FOR ('SELECT employee_id, first_name FROM '||l_sTableName);
  LOOP
    FETCH l_cTableCursor
    BULK COLLECT INTO l_tRecords
    LIMIT 1000;

    EXIT WHEN l_tRecords.COUNT = 0;

    FOR i IN 1..l_tRecords.LAST LOOP
      dbms_output.put_line(l_tRecords(i).empID||' '||l_tRecords(i).fname);
    END LOOP;
  END LOOP;
  CLOSE l_cTableCursor;
END;
/

【讨论】:

亚历克斯今天要了我的命...在我输入答案时不断提交答案。最大的区别在于将 BULK COLLECT 放入集合中以限制上下文切换。 批量收集可以减少网络往返,但会分配更多内存来保存结果(因此有限制);尽管最近的版本优化了在后台进行批量传输。 (而且我感受到了你的痛苦......我有很多这样的日子!我现在可能会安静一段时间*8-) @Del 感谢您的回答我也会尝试学习您的脚本,实际上我先播下 Alex 的答案。【参考方案2】:

您可以使用open ... for 光标语法:

declare
  v_tbl_name varchar2(30) := 'EMPLOYEES'; -- assume this will be a parameter later
  v_sql varchar2(32767) := 'select employee_id, first_name from ' || v_tbl_name;
  -- if there is a static table you could use %rowtype, or your own type with column%type
  type t_rec is record (employee_id number, first_name varchar2(20));
  v_rec t_rec; 

  c1 sys_refcursor;
begin
  open c1 for v_sql;
  loop
    fetch c1 into v_rec;
    exit when c1%notfound;
    dbms_output.put_line(v_rec.employee_id||' '||v_rec.first_name);
  end loop;
end;
/

100 Steven
101 Neena
102 Lex
103 Alexander
104 Bruce
...
205 Shelley
206 William


PL/SQL procedure successfully completed.

希望您指定您真正想要的列,而不是使用*...

根据您的应用程序/客户端,您可以直接访问引用光标;此语法适用于 SQL*Plus、SQL Developer、SQLcl 和可能的一些第三方客户端:

var rc refcursor;

declare
  v_tbl_name varchar2(200) := 'EMPLOYEES'; -- assume this will be a parameter later
  v_sql varchar2(200) := 'select * from ' || v_tbl_name;
begin
  open :rc for v_sql;
end;
/

print rc

产生如下输出:

EMPLOYEE_ID FIRST_NAME           LAST_NAME                 EMAIL                     PHONE_NUMBER         HIRE_DATE  JOB_ID         SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
----------- -------------------- ------------------------- ------------------------- -------------------- ---------- ---------- ---------- -------------- ---------- -------------
        100 Steven               King                      SKING                     515.123.4567         1987-06-17 AD_PRES         24000                                      90
        101 Neena                Kochhar                   NKOCHHAR                  515.123.4568         1989-09-21 AD_VP           17000                       100            90
        102 Lex                  De Haan                   LDEHAAN                   515.123.4569         1993-01-13 AD_VP           17000                       100            90
...
        205 Shelley              Higgins                   SHIGGINS                  515.123.8080         1994-06-07 AC_MGR          12000                       101           110
        206 William              Gietz                     WGIETZ                    515.123.8181         1994-06-07 AC_ACCOUNT       8300                       205           110

107 rows selected.

您可以从 JDBC 等访问引用光标。

【讨论】:

感谢 Alex 它对我有用,我将尝试在这个问题上应用相同的概念***.com/questions/61027238/… 你能告诉我sys_refcursor 是做什么的吗?这是一个获取样本数据以检测数据类型的数据类型或函数吗? @OsamaAl-Banna - 这是一个弱游标变量 - read more in the docs。您可以定义自己的ref cursor 变量,但使用内置的sys_refcursor 可以节省一些输入。 是的,“容易出错”警告与您的情况并不真正相关。 @OsamaAl-Banna - Yes

以上是关于如何通过 PL/SQL 过程和表名作为变量打印动态 SQL 中的所有表行?的主要内容,如果未能解决你的问题,请参考以下文章

pl/sql 过程不允许将表名/视图名作为参数传递

通过将列和表名作为参数传递来创建 BigQuery 存储过程

使用 SQL 或 PL/SQL 对多个表中的列和表名进行动态查询

如何使用绑定变量使整个 PL/SQL 代码块动态化?

PL/SQL 打印出存储过程返回的引用游标

将表名作为参数传递时 PL/SQL 函数不起作用