解决 Oracle 中的“无法在查询中执行 DML”错误

Posted

技术标签:

【中文标题】解决 Oracle 中的“无法在查询中执行 DML”错误【英文标题】:Solving "Cannot perform DML inside a query" error in Oracle 【发布时间】:2012-04-01 16:51:36 【问题描述】:

我已经创建了常规函数。它已成功创建。但是当我用

运行它时
select reg('awlad','01968688680','545466455','12345') from dual

它给了我这个错误:

ORA-14551: cannot perform a DML operation inside a query

我该如何解决这个问题?

CREATE OR REPLACE FUNCTION reg(
name in varchar2,
cellNo in varchar2,
voterId in varchar2,
pass in varchar2
)
RETURN NUMBER
IS 
succ NUMBER;
BEGIN 
      succ:=0;
      insert into logInfo values(loginfo_seq.nextval,cellNo,pass,0);
      succ:=1;
      insert into passInfo values(name,cellNo,voterId);
      succ:=2;
      RETURN succ;
END;

【问题讨论】:

【参考方案1】:

一个函数应该计算并返回一个结果,而不是改变数据库的状态。如果您想在函数中执行DML(即,如果您想向表中插入行),则不能在SELECT 语句中调用该函数,因为SELECT 语句无法更改数据库的状态。一般来说,最好将这种东西创建为存储过程而不是存储函数。

您可以像调用存储过程一样从 PL/SQL 块调用此函数

DECLARE
  l_success_code NUMBER;
BEGIN
  l_success_code := reg('awlad','01968688680','545466455','12345');
END;

如果您想将其创建为过程

CREATE OR REPLACE PROCEDURE reg( name in varchar2, 
                                 cellNo in varchar2, 
                                 voterId in varchar2, 
                                 pass in varchar2, 
                                 succ out NUMBER ) 
AS 
BEGIN 
  succ:=0; 
  insert into logInfo values(loginfo_seq.nextval,cellNo,pass,0); 
  insert into passInfo values(name,cellNo,voterId); 
  succ:=1; 
END;

那么您需要通过传入OUT 参数来调用该过程

DECLARE
  l_success_code NUMBER;
BEGIN
  reg('awlad','01968688680','545466455','12345', l_success_code);
END;

【讨论】:

感谢洞穴。我已经尝试过了,但是当我调用该程序时,它向我显示错误:调用“REG”时参数的数量或类型错误。我的程序是:CREATE OR REPLACE PROCEDURE reg(name in varchar2, cellNo in varchar2, voterId in varchar2, pass in varchar2, succ out NUMBER) AS BEGIN succ:=0;插入 logInfo 值(loginfo_seq.nextval,cellNo,pass,0);插入 passInfo 值(名称,cellNo,voterId);成功:= 1;结束; @AwladLiton - 如果您使用OUT 参数创建过程,则需要传入第五个参数。编辑了我的答案以显示如何做到这一点。【参考方案2】:

如果您只想记录信息,则使用autonomous transaction 进行中间插入是合适的。

CREATE OR REPLACE FUNCTION reg(NAME IN VARCHAR2, 
                               cellNo IN VARCHAR2, 
                               voterId IN VARCHAR2, 
                               pass IN VARCHAR2)
   RETURN NUMBER IS
   --
   PROCEDURE do_loginfo (p_id NUMBER, 
                         p_cellNo VARCHAR2, 
                         p_pass VARCHAR2, 
                         p_x NUMBER) IS
   PRAGMA AUTONOMOUS_TRANSACTION
   BEGIN
      INSERT INTO logInfo VALUES (p_id, p_cellNo, p_pass, p_x);
      COMMIT;
   END do_loginfo;
   PROCEDURE do_passInfo (p_name VARCHAR2, 
                          p_cellNo VARCHAR2, 
                          p_voterId VARCHAR2) IS
   PRAGMA AUTONOMOUS_TRANSACTION
   BEGIN
      INSERT INTO passInfo VALUES (p_name, p_cellNo, p_voterId);
      COMMIT;
   END do_passInfo;
   --
   succ NUMBER;
BEGIN
   succ := 0;
   do_logInfo (loginfo_seq.NEXTVAL, cellNo, pass, 0);
   succ := 1;
   do_passInfo (NAME, cellNo, voterId);
   succ := 2;
   RETURN succ;
END;

请注意,它对调试很有用,但由于它不是事务性的,因此不应该用于记录数据(因为即使主事务回滚,插入的行也会保留)。

【讨论】:

以上是关于解决 Oracle 中的“无法在查询中执行 DML”错误的主要内容,如果未能解决你的问题,请参考以下文章

“无法在查询中执行 DML 操作”的解决方案?

用oracle中的系统用户无法登陆问题解决方案

如何解决oracle sql中的单分组问题

oracle中的数据库乱码的原因与解决

如何解决 Oracle Apex 中的无效列错误?

Oracle数据表中的死锁情况解决方法