Oracle - 使用动态查询插入到表中

Oracle - Insert into tables using dynamic queries

我正在尝试创建一个动态查询,以安全地从一个 table 中获取 select 值,并使用 this_date 作为参数将它们插入到另一个 table 中。

因为这将从应用程序外部调用,所以我应该使用绑定变量。

table1Foo 所有。
table2Bar 所有。

我目前拥有的是:

create or replace package body Foo.this_thing
AS

procedure load_this(this_date IN date)
AS
  v_select_sql VARCHAR2(255);

  type temp_table_type IS TABLE OF Bar.table2$ROWTYPE;
  temp_table temp_table_type;

BEGIN
  -- Get data from Foo.table1
  v_select_sql := 'select :1, field1, field2 from Foo.table1 where field5 = :1';
  execute immediate v_select_sql into temp_table using this_date;
  
  -- Load from temp_table into Bar.table2
  insert into Bar.table2(attr1, attr2, attr3) select attr1, attr2, attr3 from temp_table;

  commit;
END load_this;

END Foo.this_thing;

当我尝试编译它时,出现了这个错误:

Error(101,41): PLS-00597: expression 'TEMP_TABLE' in the INTO list is of wrong type

然后我尝试了这个:

create or replace package body Foo.this_thing
AS

procedure load_this(this_date IN date)
AS
  v_sql VARCHAR2(255);

  type temp_table_type IS TABLE OF Bar.table2$ROWTYPE;
  temp_table temp_table_type;

BEGIN

  DBMS_OUTPUT.PUT_LINE('LOAD_THIS:: this_date: ' || to_char(this_date));
  
  v_sql := 'insert into Bar.table2(attr1, attr2, attr3) select :1, field1, field2 from Foo.table1 where field5 = :1';

  DBMS_OUTPUT.PUT_LINE('LOAD_THIS:: v_sql set.');
  
  execute immediate v_sql using this_date;

  DBMS_OUTPUT.PUT_LINE('LOAD_THIS:: v_sql executed.');

  commit;
END load_this;

END Foo.this_thing;

当我执行 Foo.this_thing.load_this(TO_DATE('20200629', 'YYYYMMDD')); 时,我在错误消息中得到了这个:

Error report -
SQL Error: ORA-00933: SQL command not properly ended
ORA-06512: at "Foo.THIS_THING", line 102
00933. 00000 - "SQL command not properly ended"
*Cause:
*Action:
LOAD_THIS:: this_date: 29-JUN-20
LOAD_THIS:: v_sql set.

错误消息非常含糊,我感觉它与 execeute immediate 命令有关,好像我可能没有正确使用它。

有人知道我错过了什么吗?

我很懒,所以我首先回顾了你的第二个例子。你取消了临时 table 所以它看起来比你的第一个例子更简单。

我注意到缺少“AS”关键字。所以:

create or replace package body Foo.this_thing
procedure load_this(this_date IN date)
AS
...

变成

create or replace package body Foo.this_thing
AS
procedure load_this(this_date IN date)
AS
...

我认为内部 SELECT 子句可以从

insert into Bar.table2(attr1, attr2, attr3) select :1, field1, field2 from Foo.table1 where field5 = :1

insert into table2(attr1, attr2, attr3) select field5, field1, field2 from table1 where field5 = this_date

从第一个示例中删除第二个示例中不再使用的剩余变量。

  type temp_table_type IS TABLE OF Bar.table2$ROWTYPE;
  temp_table temp_table_type;

这导致一个包在我的测试中至少在语法上是有效的。但我不能说语义的有效性。您必须为此提供更多上下文或样本数据。您可能仍然 运行 陷入基本问题,例如架构 foo 没有权限插入架构栏中的 Table2。诸如此类。

祝你好运。

您发布的代码有效,至少只要您提供两次绑定值:

execute immediate v_sql using this_date, this_date;

但你不需要动态 SQL:

procedure load_this(this_date IN date)
AS
  v_sql VARCHAR2(255);

BEGIN

  insert into table2(attr1, attr2, attr3)
  select this_date, field1, field2
  from table1 where field5 = this_date;

  commit;
END load_this;

db<>fiddle 为简单起见,将过程放在匿名块中而不是包中。

Foo does not have the privileges to insert into the table even though the role it has allows it to

如果您不想直接授予 FOO 权限,则需要对整个包使用 invoker's rights

create or replace package Foo.this_thing
AUTHID CURRENT_USER
AS

procedure load_this(this_date IN date);

END Foo.this_thing;
/

create or replace package body Foo.this_thing
AS

procedure load_this(this_date IN date)
AS
...
END load_this;

END Foo.this_thing;
/

为此您不需要动态 SQL。在这种情况下,您不必担心“安全 select 值”而值得称赞。您正在创建一个过程,其中 compiler automatically converts 参数绑定变量。

Bind Variables

When you embed a SQL INSERT, UPDATE, DELETE, MERGE, or SELECT statement directly in your PL/SQL code, the PL/SQL compiler turns the variables in the WHERE and VALUES clauses into bind variables (for details, see "Resolution of Names in Static SQL Statements"). Oracle Database can reuse these SQL statements each time the same code runs, which improves performance.

PL/SQL does not create bind variables automatically when you use dynamic SQL, but you can use them with dynamic SQL by specifying them explicitly (for details, see "EXECUTE IMMEDIATE Statement").