PL / SQL动态SQL USING子句

时间:2016-04-08 13:50:51

标签: oracle plsql dynamic-sql

我正在使用Oracle 11g数据库,版本11.2.0.3.0 - 64位生产

我有几个已定义的包,过程,函数和数据类型。在使用集合,数组和其他数据结构进行大量中间计算之后,我最终需要动态创建数据库表以输出我的最终结果。出于这个问题的目的,我有以下内容:

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来创建数据库表作为我的最终输出时,我遇到了问题:

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。

非常感谢任何建议。

1 个答案:

答案 0 :(得分:5)

您展示的代码未获得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

因此,您需要分两步创建和填充表格:

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

动态创建表通常不是一个好主意;除了模式管理和可维护性考虑之外,您必须确保只有一个会话正在调用该过程并且该表尚不存在。如果你有一个完成这项工作的进程,使用结果然后删除表,那么你仍然必须确保它不能同时运行,并且如果它在一半时间内失败就可以重新启动。

如果所有工作都在同一会话中完成,那么您可以创建一个(永久)全局临时表,作为一次性架构设置任务。填充它的插入仍然必须是动态的,因为in_table_nme未知,但它会有一点改进。 (我不确定为什么find_max_ids中的查询是动态的,除非您还动态创建benefits。或者,根据所涉及的数据量,您可以使用其他集合类型,而不是表格。

GTT中的数据仅对该会话可见,并在结束时被销毁。如果这不合适,则可以创建一次普通表,这比动态创建/删除它更好。在这种情况下,您仍然需要阻止同时运行该进程的多个会话,因为他们可能看不到他们期望的数据。