Oracle - 使用动态查询插入表
Posted
技术标签:
【中文标题】Oracle - 使用动态查询插入表【英文标题】:Oracle - Insert into tables using dynamic queries 【发布时间】:2020-06-29 19:42:31 【问题描述】:我正在尝试创建一个动态查询,以安全地从一个表中选择值并将它们插入到另一个表中,并使用 this_date
作为参数。
因为这将从应用程序外部调用,所以我应该使用绑定变量。
table1
归 Foo
所有。table2
归 Bar
所有。
到目前为止我所拥有的是:
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学习笔记(4-7)多表联合查询,子查询,动态条件查询