我编写了一个 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 others avg count current exists max min prior sql stddev sum variance execute forall time timestamp interval日期
此问题出现在 8i (8.1.6) 上,但不是 10g。如果 BEGIN .. END 块被删除 - 问题就消失了。如果查询中的子选择被其他东西(即常量)替换,问题就消失了。
不幸的是,我坚持使用 8i 并删除 BEGIN .. END 不是一个选项(参见上面的解释)。
这里有特定的 Oracle 8i 限制吗?是否可以使用动态 SQL 来克服它?