PL/SQL 动态 SQL USING 子句
PL/SQL Dynamic SQL USING clause
我正在使用 Oracle 11g 数据库,版本 11.2.0.3.0 - 64 位生产
我有几个已定义的包、过程、函数和数据类型。在主要使用集合、数组和其他数据结构完成大量中间计算之后,我最终需要动态创建一个数据库 table 来输出我的最终结果。为了这个问题的目的,我有以下内容:
TYPE ids_t IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
benefit_ids ids_t;
--Lots of other code which successfully populates benefit_ids.
--benefit_ids has several million rows, and is used successfully as
the input to the following function:
FUNCTION find_max_ids(in_ids in ids_t)
RETURN ids_t
IS
str_sql varchar2(200);
return_ids ids_t;
BEGIN
str_sql := 'SELECT max(b.benefit_id)
FROM TABLE(:1) a
JOIN benefits b ON b.benefit_id = a.column_value
GROUP BY b.benefit_id';
EXECUTE IMMEDIATE str_sql BULK COLLECT INTO return_ids USING in_ids;
RETURN return_ids;
END;
以上工作正常,清楚地表明可以将数组作为参数传递给动态 sql 函数或过程。
但是,当我尝试使用 EXECUTE IMMEDIATE 和 USING 创建数据库 table 作为我的最终输出时,我 运行 遇到了问题:
PROCEDURE create_output_table(in_ids in ids_t, in_tbl_nme in varchar2)
AUTHID CURRENT_USER
IS
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(:1) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
END;
我收到的唯一错误消息毫无帮助是 ORA-00933:SQL 命令未正确结束。但是,我看不出语法本身有什么问题,尽管我怀疑问题出在我在这种情况下如何应用 EXECUTE IMMEDIATE。
如有任何建议,我们将不胜感激。
您显示的代码没有得到 ORA-00933,但它仍然无效:
create type ids_t is table of number
/
create table test_table (client_id number, benefit_id number)
/
insert into test_table values (1, 1)
/
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(:1) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
Error report -
ORA-22905: cannot access rows from a non-nested table item
那个错误看起来不对;让我们施放它看看它是否更快乐,即使它不应该是必要的:
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(CAST(:1 AS ids_t)) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
Error report -
ORA-01027: bind variables not allowed for data definition operations
那个错误is described in this article。
因此您需要分两步创建和填充 table:
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
WHERE 1=0'; -- or anything that always evaluates to false
EXECUTE IMMEDIATE str_sql;
str_sql := 'INSERT INTO Final_Results (client_id, benefit_id)
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(CAST(:1 AS ids_t)) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
PL/SQL procedure successfully completed.
select * from final_results;
CLIENT_ID BENEFIT_ID
---------- ----------
1 1
临时创建 table 通常不是一个好主意;除了模式管理和可维护性方面的考虑,您必须确保只有一个会话正在调用该过程并且 table 不存在。如果您有一个执行此工作的进程,使用结果然后删除 table,那么您仍然必须确保它不能同时为 运行,并且如果它中途失败可以重新启动。
如果所有工作都在同一个会话中完成,那么您可以创建一个(永久的)全局临时 table 作为一次性模式设置任务。填充它的插入仍然必须是动态的,因为 in_table_nme
未知,但这会有所改进。 (我不确定为什么你在 find_max_ids
中的查询是动态的,除非你也在动态创建 benefits
)。或者根据涉及的数据量,您可以使用另一种集合类型,而不是 table.
GTT 中的数据仅对该会话可见,并在结束时销毁。如果这不合适,那么可以创建一次普通的 table,这比动态创建 creating/dropping 更好。不过,在这种情况下,您仍然需要防止多个会话 运行 同时处理该过程,因为他们可能看不到他们期望的数据。
我正在使用 Oracle 11g 数据库,版本 11.2.0.3.0 - 64 位生产
我有几个已定义的包、过程、函数和数据类型。在主要使用集合、数组和其他数据结构完成大量中间计算之后,我最终需要动态创建一个数据库 table 来输出我的最终结果。为了这个问题的目的,我有以下内容:
TYPE ids_t IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
benefit_ids ids_t;
--Lots of other code which successfully populates benefit_ids.
--benefit_ids has several million rows, and is used successfully as
the input to the following function:
FUNCTION find_max_ids(in_ids in ids_t)
RETURN ids_t
IS
str_sql varchar2(200);
return_ids ids_t;
BEGIN
str_sql := 'SELECT max(b.benefit_id)
FROM TABLE(:1) a
JOIN benefits b ON b.benefit_id = a.column_value
GROUP BY b.benefit_id';
EXECUTE IMMEDIATE str_sql BULK COLLECT INTO return_ids USING in_ids;
RETURN return_ids;
END;
以上工作正常,清楚地表明可以将数组作为参数传递给动态 sql 函数或过程。
但是,当我尝试使用 EXECUTE IMMEDIATE 和 USING 创建数据库 table 作为我的最终输出时,我 运行 遇到了问题:
PROCEDURE create_output_table(in_ids in ids_t, in_tbl_nme in varchar2)
AUTHID CURRENT_USER
IS
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(:1) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
END;
我收到的唯一错误消息毫无帮助是 ORA-00933:SQL 命令未正确结束。但是,我看不出语法本身有什么问题,尽管我怀疑问题出在我在这种情况下如何应用 EXECUTE IMMEDIATE。
如有任何建议,我们将不胜感激。
您显示的代码没有得到 ORA-00933,但它仍然无效:
create type ids_t is table of number
/
create table test_table (client_id number, benefit_id number)
/
insert into test_table values (1, 1)
/
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(:1) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
Error report -
ORA-22905: cannot access rows from a non-nested table item
那个错误看起来不对;让我们施放它看看它是否更快乐,即使它不应该是必要的:
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS (
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(CAST(:1 AS ids_t)) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL)';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
Error report -
ORA-01027: bind variables not allowed for data definition operations
那个错误is described in this article。
因此您需要分两步创建和填充 table:
declare
str_sql varchar2(4000);
in_tbl_nme varchar2(30) := 'TEST_TABLE';
in_ids ids_t := ids_t(1, 2, 3);
begin
str_sql := 'CREATE TABLE Final_Results AS
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
WHERE 1=0'; -- or anything that always evaluates to false
EXECUTE IMMEDIATE str_sql;
str_sql := 'INSERT INTO Final_Results (client_id, benefit_id)
SELECT a.client_id, a.benefit_id
FROM ' || in_tbl_nme || ' a
LEFT JOIN TABLE(CAST(:1 AS ids_t)) b on b.column_value = a.benefit_id
WHERE b.column_value is NOT NULL';
EXECUTE IMMEDIATE str_sql USING IN in_ids;
end;
/
PL/SQL procedure successfully completed.
select * from final_results;
CLIENT_ID BENEFIT_ID
---------- ----------
1 1
临时创建 table 通常不是一个好主意;除了模式管理和可维护性方面的考虑,您必须确保只有一个会话正在调用该过程并且 table 不存在。如果您有一个执行此工作的进程,使用结果然后删除 table,那么您仍然必须确保它不能同时为 运行,并且如果它中途失败可以重新启动。
如果所有工作都在同一个会话中完成,那么您可以创建一个(永久的)全局临时 table 作为一次性模式设置任务。填充它的插入仍然必须是动态的,因为 in_table_nme
未知,但这会有所改进。 (我不确定为什么你在 find_max_ids
中的查询是动态的,除非你也在动态创建 benefits
)。或者根据涉及的数据量,您可以使用另一种集合类型,而不是 table.
GTT 中的数据仅对该会话可见,并在结束时销毁。如果这不合适,那么可以创建一次普通的 table,这比动态创建 creating/dropping 更好。不过,在这种情况下,您仍然需要防止多个会话 运行 同时处理该过程,因为他们可能看不到他们期望的数据。