【问题标题】:cannot perform DML operation inside a query无法在查询中执行 DML 操作
【发布时间】:2011-05-04 22:46:54
【问题描述】:

我无法说服为什么我不能在 Oracle 函数 中添加 DML 操作,尤其是在 游标循环 中。我感觉Oracle不支持游标循环内的DML操作。

如果我需要在游标循环内插入表格,我该怎么办?在其中创建新的存储过程或其他什么?

错误消息:无法在查询中执行 DML 操作

这是我的功能,

CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
  V_MESSAGE VARCHAR2(30);
  CURSOR C_PERSON (V_ID VARCHAR2) IS
         SELECT NAME_UPPER
         FROM TBL_PERSON
         WHERE NAME_UPPER = V_ID;                  
BEGIN
   FOR C_PERSON_CURSOR IN C_PERSON(U_ID) 
   LOOP
       INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
   END LOOP;

   RETURN V_MESSAGE;

EXCEPTION
WHEN OTHERS THEN
    raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    您可以在 PL/SQL 函数中使用 DML - 没问题。但是,该函数只能从 PL/SQL 调用,而不能从 SQL 调用 - 即它可以这样调用:

    declare
       l_message varchar2(30);
    begin
       l_message := test_func('123');
    end;
    

    ...但不是这样的:

    select test_func(empno) from emp;
    

    这会导致您发布的错误消息。

    很多人(包括我)不喜欢这样具有“副作用”的功能,但这是最佳实践和标准问题,而不是技术问题。

    【讨论】:

    • 谢谢,这就是我想要的。
    【解决方案2】:

    您可以在 Oracle PL/SQL 函数中执行 DML 操作,并且虽然这通常不是一个好的做法,但可以从 SQL 调用它。该函数必须用 pragma AUTONOMOUS_TRANSACTION 进行标记,并且必须在退出函数之前提交或回滚事务(请参阅AUTONOMOUS_TRANSACTION Pragma)。

    您应该知道,从 SQL 调用的这种函数会大大降低您的查询性能。我建议您仅将其用于审核目的。

    这是一个从您的函数开始的示例脚本:

    创建表 TBL_PERSON (NAME_UPPER VARCHAR2(30)); 创建表 TMP_PERSON (NAME VARCHAR2(30)); 插入 TBL_PERSON (NAME_UPPER) 值 ('KING'); 创建或替换函数 TEST_FUNC(U_ID IN VARCHAR2) 返回 VARCHAR2 是 PRAGMA AUTONOMOUS_TRANSACTION; -- 需要从 SQL 调用 V_MESSAGE VARCHAR2(2000); CURSOR C_PERSON (V_ID VARCHAR2) 是 选择 NAME_UPPER FROM TBL_PERSON WHERE NAME_UPPER = V_ID; 开始 FOR C_PERSON_CURSOR IN C_PERSON(U_ID) 环形 插入 TMP_PERSON(NAME) 值 (C_PERSON_CURSOR.NAME_UPPER); V_MESSAGE := SQL%ROWCOUNT || ' 个人记录成功插入到 TMP_PERSON 表中'; 结束循环; 犯罪; -- 当前自治事务需要提交 -- 在退出函数之前。 返回 V_MESSAGE; 例外 其他时候 回滚; raise_application_error(-20001,'遇到错误 - '||SQLCODE||' -ERROR- '||SQLERRM); 结尾; / PROMPT 调用 TEST_FUNC 函数并在 TMP_PERSON 表中插入一条新记录 从 DUAL 中选择 TEST_FUNC('KING'); PROMPT TMP_PERSON 表的内容 A30 的颜色名称 从 TMP_PERSON 中选择 *;

    在运行之前的脚本时,我们得到以下输出:

    表已创建。 表已创建。 创建了 1 行。 创建的函数。 调用 TEST_FUNC 函数并在 TMP_PERSON 表中插入一条新记录 TEST_FUNC('KING') -------------------------------------------------- ---------- 1 个人记录成功插入到 TMP_PERSON 表中 TMP_PERSON 表的内容 姓名 ------------------------------ 国王

    【讨论】:

    • +1。除了性能和事务问题之外,也很难确切知道函数将执行多少次。例如,这条语句根本不调用函数:select * from dual where exists (select test_func('KING') from dual); SQL 是声明性的,无法准确保证查询将如何执行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-05
    • 2012-02-02
    • 1970-01-01
    • 2011-11-10
    • 2016-11-08
    • 1970-01-01
    相关资源
    最近更新 更多