如何通过 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?为了在下面的代码中进行测试,我正在使用HR
schema
中的演示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 中的所有表行?的主要内容,如果未能解决你的问题,请参考以下文章
通过将列和表名作为参数传递来创建 BigQuery 存储过程