无法在查询中执行DML操作

PPS*_*ein 6 sql oracle

我无法说服为什么我不能在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;
Run Code Online (Sandbox Code Playgroud)

Ton*_*ews 13

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

declare
   l_message varchar2(30);
begin
   l_message := test_func('123');
end;
Run Code Online (Sandbox Code Playgroud)

......但不是这样的:

select test_func(empno) from emp;
Run Code Online (Sandbox Code Playgroud)

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

许多人(包括我)不喜欢具有这种"副作用"的功能,但这是最佳实践和标准问题,而不是技术问题.


Bru*_*ier 11

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

您应该知道从SQL调用的这种函数会极大地降低您的查询性能.我建议您将其用于审计目的.

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

CREATE TABLE TBL_PERSON (NAME_UPPER VARCHAR2(30));
CREATE TABLE TMP_PERSON (NAME VARCHAR2(30));

INSERT INTO TBL_PERSON (NAME_UPPER) VALUES ('KING');

CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
  PRAGMA AUTONOMOUS_TRANSACTION; -- Needed to be called from SQL

  V_MESSAGE VARCHAR2(2000);
  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);

       V_MESSAGE := SQL%ROWCOUNT
          || ' Person record successfully inserted into TMP_PERSON table';
   END LOOP;

   COMMIT; -- The current autonomous transaction need to be commited
           -- before exiting the function.

   RETURN V_MESSAGE;

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

PROMPT Call the TEST_FUNC function and insert a new record into TMP_PERSON table
SELECT TEST_FUNC('KING') FROM DUAL;

PROMPT Content of the TMP_PERSON table
COL NAME FOR A30
SELECT * FROM TMP_PERSON;

运行上一个脚本时,我们得到以下输出:

Table created.

Table created.

1 row created.

Function created.

Calling the TEST_FUNC function and insert a new record into TMP_PERSON table

TEST_FUNC('KING')
------------------------------------------------------------
1 Person record successfully inserted into TMP_PERSON table

Content of the TMP_PERSON table

NAME
------------------------------
KING

  • +1.除了性能和事务问题之外,很难确切地知道函数执行的次数.例如,此语句根本不调用该函数:select*from dual where exists(从double选择test_func('KING')); SQL是声明性的,没有办法确切地保证查询的执行方式. (2认同)