Oracle - 使用动态查询插入表

Posted

技术标签:

【中文标题】Oracle - 使用动态查询插入表【英文标题】:Oracle - Insert into tables using dynamic queries 【发布时间】:2020-06-29 19:42:31 【问题描述】:

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

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

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;

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

错误(101,41):PLS-00597:INTO 列表中的表达式“TEMP_TABLE”类型错误

然后我尝试了这个:

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')); 时,我在错误消息中得到了这个:

错误报告 - SQL 错误:ORA-00933:SQL 命令未正确结束 ORA-06512:在“Foo.THIS_THING”,第 102 行 00933. 00000 - “SQL 命令未正确结束” *原因: *行动: LOAD_THIS:: this_date: 29-JUN-20 LOAD_THIS:: v_sql 设置。

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

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

【问题讨论】:

你为什么要为此使用动态 SQL? @AlexPoole 我正在为此使用动态 SQL,因此我可以保护数据库不成为 SQL 注入的受害者。 您不需要在包中使用动态 SQL 来执行此操作。当您在查询中使用传递给过程的参数时,它们实际上是绑定变量。 【参考方案1】:

只要您提供两次绑定值,您发布的代码就可以工作:

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 没有权限插入到表中,即使它拥有的角色允许它

如果您不想直接将权限授予 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;
/

【讨论】:

好的....这导致了我的第二个问题。我正在使用基于角色的特权,并且 Foo 被授予 FOO_UPDATE 特权,该特权已在 Bar.table2 上授予 SELECT, INSERT, UPDATE, DELETE 特权;但是,当我尝试像insert into Bar.table2(...) select ... from Foo.table1; 一样进行查询时,包不会编译,因为它说Foo 没有权限插入到表中,即使它的角色允许它...你知道如何在不明确授予Foo 权限的情况下实现它吗? @Sometowngeek - 包必须有 invoker's rights 才能从角色中获取权限。 抱歉,耽搁了这么久……我上周在工作中开始了一个新的 Sprint,但没有关于这个的故事。我将为下一个 Sprint 创建一个并跟进。 再次,对于超长的延迟感到抱歉......我们最终将这个项目推到了积压工作中。但我确实遇到了另一个与这个问题相同的项目。这解决了我的问题!非常感谢,亚历克斯!【参考方案2】:

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

绑定变量

当您嵌入 SQL INSERT、UPDATE、DELETE、MERGE 或 SELECT 直接在您的 PL/SQL 代码中声明,PL/SQL 编译器会将 WHERE 和 VALUES 子句中的变量转换为绑定变量(对于 详细信息,请参阅“静态 SQL 语句中的名称解析”)。甲骨文 每次运行相同的代码时,数据库都可以重用这些 SQL 语句, 这提高了性能。

PL/SQL 使用时不会自动创建绑定变量 动态 SQL,但您可以通过指定它们来将它们与动态 SQL 一起使用 显式(详情参见“EXECUTE IMMEDIATE 语句”)。

【讨论】:

【参考方案3】:

我很懒,所以我从查看您的第二个示例开始。您取消了临时表,因此总体上看起来比您的第一个示例更简单。

我注意到缺少“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。诸如此类。

祝你好运。

【讨论】:

以上是关于Oracle - 使用动态查询插入表的主要内容,如果未能解决你的问题,请参考以下文章

使用动态表名称查询 Oracle 数据库

Oracle曾经的Oracle学习笔记(4-7)多表联合查询,子查询,动态条件查询

具有动态表名的 Oracle Select 查询

Oracle - 匿名过程循环遍历多个表(动态) - 查询返回多行

oracle动态查询通过sql获取游标变量

使用动态列 Oracle SQL 查询的数据