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 更好。不过,在这种情况下,您仍然需要防止多个会话 运行 同时处理该过程,因为他们可能看不到他们期望的数据。