为啥我在更新语句后使用 RETURNING 子句时收到“NO DATA FOUND”异常?

Posted

技术标签:

【中文标题】为啥我在更新语句后使用 RETURNING 子句时收到“NO DATA FOUND”异常?【英文标题】:Why I receive "NO DATA FOUND" exception when I use RETURNING clause after update statement?为什么我在更新语句后使用 RETURNING 子句时收到“NO DATA FOUND”异常? 【发布时间】:2017-02-02 15:53:57 【问题描述】:

我正在编写一个简单的 LOOP FOR 代码,以使用关联数组中填充的信息更新表中的列。当我只使用 UPDATE 语句时,一切似乎都是正确的,但是当我添加一个 RETURNING 子句时,我收到“NO DATA FOUND”错误。谢谢!

DECLARE
  TYPE emps_info IS TABLE OF employees23%ROWTYPE
    INDEX BY PLS_INTEGER;

  t_emps_current_info emps_info;
  t_emps_new_info     emps_info;
BEGIN
  SELECT *
  BULK COLLECT INTO t_emps_current_info
  FROM   employees;

  FOR emps_index IN t_emps_current_info.FIRST .. t_emps_current_info.LAST
  LOOP
    IF
      NVL(t_emps_current_info(emps_index).commission_pct, 0) = 0 THEN
        UPDATE employees23
        SET    commission_pct = 0.3
        WHERE  employee_id = t_emps_current_info(emps_index).employee_id;
    ELSIF
      t_emps_current_info(emps_index).commission_pct BETWEEN 0.1 AND 0.3 THEN
        UPDATE employees23
        SET    commission_pct = 0.5
        WHERE  employee_id = t_emps_current_info(emps_index).employee_id;
    END IF;
  END LOOP;
END;

现在当我添加 RETURNING 子句时,我收到以下错误:

DECLARE
  TYPE emps_info IS TABLE OF employees23%ROWTYPE
    INDEX BY PLS_INTEGER;

  t_emps_current_info emps_info;
  t_emps_new_info     emps_info;
BEGIN
  SELECT *
  BULK COLLECT INTO t_emps_current_info
  FROM   employees;

  FOR emps_index IN t_emps_current_info.FIRST .. t_emps_current_info.LAST
  LOOP
    IF
      NVL(t_emps_current_info(emps_index).commission_pct, 0) = 0 THEN
        UPDATE employees23
        SET    commission_pct = 0.3
        WHERE  employee_id = t_emps_current_info(emps_index).employee_id
        RETURNING commission_pct
        INTO      t_emps_new_info(emps_index).commission_pct;
    ELSIF
      t_emps_current_info(emps_index).commission_pct BETWEEN 0.1 AND 0.3 THEN
        UPDATE employees23
        SET    commission_pct = 0.5
        WHERE  employee_id = t_emps_current_info(emps_index).employee_id
        RETURNING commission_pct
        INTO      t_emps_new_info(emps_index).commission_pct;
    END IF;

    DBMS_OUTPUT.PUT_LINE('EMPLOYEE_ID: '                                || 
                         t_emps_current_info(emps_index).employee_id    ||
                         ' OLD COMMISSION: '                            || 
                         NVL(t_emps_current_info(emps_index).commission_pct, 0) 
                         || ' NEW COMMISSION: '                            ||
                         t_emps_new_info(emps_index).commission_pct);
  END LOOP;
END;

通知错误 - ORA-01403: 未找到数据 ORA-06512: 在第 22 行 01403. 00000 - “未找到数据” *原因:没有从对象中找到数据。 *操作:对象中没有数据,可能是由于提取结束。

【问题讨论】:

【参考方案1】:

代码中的这部分

DBMS_OUTPUT.PUT_LINE('EMPLOYEE_ID: '                                || 
                         t_emps_current_info(emps_index).employee_id    ||
                         ' OLD COMMISSION: '                            || 
                         NVL(t_emps_current_info(emps_index).commission_pct, 0) 
                         || ' NEW COMMISSION: '                            ||
                         t_emps_new_info(emps_index).commission_pct);

打印原始数组和返回数组。

在更新期间的某个时间点,您的更新语句可能不会更新任何行,在这种情况下,您的原始数组将在该索引处具有值,但您返回的数组不会

所以检查索引是否存在以避免这个错误

case when t_emps_new_info.exists(emps_index) then
 dbms_output.put_line('print something') ;
 else  dbms_output.put_line('print something else') ;
 end case;

【讨论】:

【参考方案2】:

您得到 NO_DATA_FOUND 是因为您在代码中没有任何地方初始化(创建)t_emps_new_info(emps_index) 记录 - 每当您的代码尝试分配 t_emps_new_info(emps_index).commission_pct 时,它都会失败。

【讨论】:

【参考方案3】:

我不确定为什么必须这样做,但我会将每个 UPDATE 语句的返回值更改为一个简单的局部变量。然后我将表记录字段设置为简单变量。

可能不相关,但我注意到您的 ELSIF 测试没有像您的 IF 测试那样对现有的 t_emps_current_info(emps_index).commission_pct 执行 NVL()。

如果是我,我会记录 IF-ELSEIF 两侧的更新,以查看一个更新是否有效而另一个更新失败。也许它只是在 ELSIF 上失败了,因为缺少 NVL()?

【讨论】:

以上是关于为啥我在更新语句后使用 RETURNING 子句时收到“NO DATA FOUND”异常?的主要内容,如果未能解决你的问题,请参考以下文章

防止 NHibernate 将 Returning 子句添加到生成的插入语句中

动态SQL(章节摘要)

Oracle 是不是支持 SQL 语句中的 RETURNING?

在 PL/SQL Developer 中使用了没有 Where 子句的更新语句

使用 FORALL 和 RETURNING 插入表时如何获取 ROWID

PostgreSQL RETURNING 失败并出现 REGEXP_REPLACE