pl/sql 块中的子选择上的 Oracle 8i 动态 SQL 错误
Posted
技术标签:
【中文标题】pl/sql 块中的子选择上的 Oracle 8i 动态 SQL 错误【英文标题】:Oracle 8i dynamic SQL error on subselects in pl/sql blocks 【发布时间】:2012-04-02 15:58:37 【问题描述】:我编写了一个 Oracle 函数(用于 8i)来获取受 DML 语句影响的行,模拟来自 PostgreSQL 的 RETURNING * 的行为。典型的函数调用如下所示:
SELECT tablename_dml('UPDATE tablename SET foo = ''bar''') FROM dual;
该函数是为每个表自动创建的,并使用动态 SQL 执行作为参数传递的查询。此外,动态执行查询的语句也包含在 BEGIN .. END 块中:
EXECUTE IMMEDIATE 'BEGIN'||query||' RETURNING col1, col2 BULK COLLECT INTO :1, :2;END;' USING OUT col1_t, col2_t;
这种特殊构造背后的原因是,它似乎是从影响多行的 DML 语句中获取值的唯一方法。 col1_t 和 col2_t 都声明为与表列对应的类型的集合。
最后,解决问题。当传递的查询包含子选择时,函数的执行会产生语法错误。下面是一个简单的例子来说明这一点:
CREATE TABLE xy(id number, name varchar2(80));
CREATE OR REPLACE FUNCTION xy_fn(query VARCHAR2) RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'BEGIN '||query||'; END;';
ROLLBACK;
RETURN 5;
END;
SELECT xy_fn('update xy set id = id + (SELECT min(id) FROM xy)') FROM DUAL;
最后一条语句产生以下错误:(那里提到的 SELECT 是 SELECT min(id))
ORA-06550:第 1 行,第 32 列:PLS-00103:遇到符号 “SELECT”当期望以下之一时:( - + mod not null 其他 avg count 当前存在 max min prior sql stddev sum 方差执行forall时间时间戳间隔日期
此问题发生在 8i (8.1.6) 上,但不是 10g。 如果 BEGIN .. END 块被删除 - 问题就消失了。 如果查询中的子选择被替换为其他内容,即常量,问题就会消失。
不幸的是,我坚持使用 8i 并删除 BEGIN .. END 不是一个选项(请参阅上面的解释)。
这里有特定的 Oracle 8i 限制吗?是否可以通过动态 SQL 来克服它?
【问题讨论】:
8i??延长支持已于 5 年前结束... @Ben:你可以笑,但我仍在对 8i 数据库实例进行更改... @JeffreyKemp,我没笑!我知道有时很难移动(我还有一些相互连接的 9i 盒子 - 但我们已经升级了它们)。如果真的应该使用受支持的版本……这比使用不受支持的版本更有意义。 @ben - 有问题的版本是 8.1.6,甚至不是 8i 的终端版本。所以无论如何都没有资格获得扩展支持。 我实际上正在将数据迁移到 PostgreSQL,但我仍然需要 8i 来保证转换的质量。 【参考方案1】:不知道为什么你需要做所有这些工作。 Oracle 8i 支持 RETURNING INTO 与批量收集。 Find out more
所以您应该能够在非动态 SQL 中执行此语句。像这样的:
UPDATE tablename
SET foo = 'bar'
returning col1, col2 bulk collect into col1_t, col2_t;
【讨论】:
我有很多针对同一张表的查询。我想避免创建匿名 pl/sql 块,将它们包装在一个可以为每个查询调用的过程中。鉴于此,我必须动态执行传递给此类过程的查询。【参考方案2】:去掉所有无关紧要的东西,我认为你的问题很简单。
此更新语句在 SQL 中运行:
update xy set id = id + (SELECT min(id) FROM xy);
而且这个匿名块也运行:
begin
update xy set id = id + 100;
end;
但是将两者结合起来是行不通的:
begin
update xy set id = id + (SELECT min(id) FROM xy);
end;
您可能遇到了旧版 Oracle 的限制。在 9i 之前,SQL 引擎和 PL/SQL SQL 引擎总是不同步。因此,SQL 支持的最新特性在 PL/SQL 中通常不被支持。好像你有其中之一。
自从 9i 以来,Oracle 一直在努力使这两个引擎保持同步,因此很难找到在 SQL 中工作但在 PL/SQL 中不工作的东西。
鉴于您的任务性质,升级您的 Oracle 版本已经结束。所以我只能建议你有两个过程,一个支持子查询语法(通过避免对此类子查询的需要。像这样:
CREATE OR REPLACE FUNCTION xy_sqfn
(main_query VARCHAR2
, sub_query VARCHAR2 )
RETURN NUMBER
IS
n pls_integer;
BEGIN
execute immediate sub_query into n;
EXECUTE IMMEDIATE 'BEGIN '||main_query||'; END;'
using n;
RETURN 5;
END;
这样称呼
result := xy_sqfn ('update xy set id = id + :1'
, 'SELECT min(id) FROM xy');
现在这种方法不适用于相关子查询。因此,如果您拥有其中任何一个,您将需要再次做一些不同的事情。
顺便说一句,使用 AUTONOMOUS TRANSACTION 杂注来伪造在 SELECT 语句中执行 DML 是非常可怕的。为什么不直接在 PL/SQL 中运行函数呢?还是使用程序?我想您会说这没关系,因为您只是在编写一些笨拙的代码来支持数据迁移。这很公平,但为了未来寻求者的利益:不要这样做!这是非常糟糕的做法!
【讨论】:
谢谢,我需要选择和自治事务的原因是来自 DML 语句的数据应该来自 python 应用程序,该应用程序将结果与来自 PostgreSQL 数据库的类似结果进行比较。考虑到将 DML 语句包装在函数中的方法的所有障碍以及 SQL 和 pl/sql 引擎之间的 8i 不一致问题,我将根据您之前的答案转移到匿名 pl/sql 块,使用主机变量来收集结果。祝我好运:-)以上是关于pl/sql 块中的子选择上的 Oracle 8i 动态 SQL 错误的主要内容,如果未能解决你的问题,请参考以下文章
类似于 Oracle PL/SQL 块中的 finally Block (JAVA)