Oracle PL/SQL - ORA-01403 使用“SELECT INTO”时“未找到数据”

Posted

技术标签:

【中文标题】Oracle PL/SQL - ORA-01403 使用“SELECT INTO”时“未找到数据”【英文标题】:Oracle PL/SQL - ORA-01403 "No data found" when using "SELECT INTO" 【发布时间】:2014-03-28 09:05:18 【问题描述】:

我在 Oracle 中开发触发器时遇到了这个问题:ORA-01403: no data found。我做了一些研究并了解了问题的根源。不过handling the error exception 阻止了上述错误,但并没有解决我的问题。

我目前正在寻找的是一种最佳解决方法,以执行较少的查询量/尽可能实现最佳性能。我将尝试描述为真实结构创建简单示例的场景。

场景

我有一个“日期参考”表来确定时间段,比如:

CREATE TABLE DATE_REFERENCE (
    DATE_START                  DATE NOT NULL,
    DATE_END                    DATE NOT NULL,
    -- Several other columns here, this is just a silly example
    CONSTRAINT PK_DATE_REFERENCE PRIMARY KEY(DATE_START, DATE_END)
);

当触发器被触发时,我将有一个 DATE 字段 - 例如 DATE_GIVEN(例如缘故)。我需要的是:

    找到DATE_REFERENCE所在的行DATE_GIVEN BETWEEN DATE_START AND DATE_END(简单); 如果上一个选项返回无数据,我需要找到下一个最接近DATE_STARTDATE_GIVEN

在这两种情况下,我都需要从表 DATE_REFERENCE 中检索包含所有列的行,无论它是否匹配选项 1 或 2。这正是我遇到所描述问题的地方。 p>

我把这个测试块写到 test 并试图找到一个解决方案。我知道,下面的示例不起作用但这正是我想要完成的(在概念上)。我添加了像 -- Lots of code 这样的 cmets,以明确这将成为更复杂触发器的一部分:

DECLARE
    DATE_GIVEN       DATE; 
    RESULTROW        DATE_REFERENCE%ROWTYPE;
BEGIN

    -- Lots of code
    -- Lots of code
    -- Lots of code

    DATE_GIVEN := TO_DATE('2014-02-26 12:30:00', 'YYYY-MM-DD HH24:MI:SS');

    -- This one throws the ORA-01403 exception if no data was found
    SELECT 
       * INTO RESULTROW
    FROM
       DATE_REFERENCE
    WHERE
       DATE_GIVEN BETWEEN DATE_START AND DATE_END;

    -- If the above didn't throw exceptions, I would continue like so:
    IF RESULTROW IS NULL THEN

        SELECT 
           * INTO RESULTROW
        FROM
           DATE_REFERENCE
        WHERE
           DATE_START > DATE_GIVEN
           AND ROWNUM = 1
        ORDER BY DATE_START ASC;

    END IF;

    -- Now RESULTROW is populated, and the rest of the trigger code gets executed ~beautifully~

    -- Lots of code
    -- Lots of code
    -- Lots of code

END;

问题

知道上面的 PL/SQL 块更多的是一个概念而不是工作代码,什么是获得RESULTROW 填充、注意性能和尽可能少的查询的最佳方式?

抱歉这个问题太长了,但我认为情景解释是必要的。提前感谢您的任何帮助/想法!

【问题讨论】:

另见***.com/questions/1256112/… 异常处理 【参考方案1】:

只需使用 ordering 和 rownum 直接填充该字段:

SELECT * INTO RESULTROW
FROM (SELECT *
      FROM DATE_REFERENCE
      ORDER BY (CASE WHEN DATE_GIVEN BETWEEN DATE_START AND DATE_END
                     THEN 1 ELSE 0
                END) DESC,
               (DATE_START - DATE_GIVEN)
     ) t
WHERE rownum = 1;

这将使用一个查询填充信息。

编辑:

如果要在子查询中放入条件,则需要:

SELECT * INTO RESULTROW
FROM (SELECT *
      FROM DATE_REFERENCE
      WHERE DATE_GIVEN <= DATE_END
      ORDER BY (CASE WHEN DATE_GIVEN BETWEEN DATE_START AND DATE_END
                     THEN 1 ELSE 0
                END) DESC,
               (DATE_START - DATE_GIVEN)
     ) t
WHERE rownum = 1;

我相信正确的条件是DATE_GIVEN &lt;= DATE_END。这涵盖了between 条件并且应该暗示DATE_GIVEN &lt; DATE_START。这假定DATE_END 永远不是NULL

【讨论】:

我会将WHERE DATE_END &gt;= DATE_GIVEN 添加到内部查询中。 Althougn OP 没有提到表的大小,假设这种情况可能会提高性能(至少不会降低)。 很棒的解决方案,没想到。它只需要一个修复:正如@YaroslavShabalin 建议的那样,WHERE DATE_END &gt;= DATE_GIVEN 不仅可以提高性能,因为它需要选择正确的DATE_REFERENCE 记录。例如。 (DATE_START - DATE_GIVEN) 可能会呈现负值,因此如果 BETWEEN 条件不匹配,我们可以获得第一个句点而不是“下一个”句点。 @YaroslavShabalin 。 . .是的,可以添加该条件。它确实假设DATE_END &gt;= DATE_START,这是合理的,并且DATE_END 不是NULL【参考方案2】:

我也遇到过类似的问题,这样解决的:

如果该行在表 LADDER.INCR_PROCESS 中不存在,我将 IsPassed 设为 Null:

Declare    
    IsPassed      Integer ;
Begin
    Select I.LVL Into IsPassed 
      From LADDER.INCR_PROCESS  I
      Right 
      Join  Dual   on I.LVL >= 90010  and I.Passed = 0  
     Where RowNum = 1 ;
     ....
End;

【讨论】:

【参考方案3】:

我刚刚找到了新的解决方案:

    Declare
        L_ID    Integer;
      Begin
      select 
          (  Select PREFERREDID  from ladder.ric_exceptions where  PREFERREDID  = 1 ) into  L_ID
      from dual;
      end;

【讨论】:

以上是关于Oracle PL/SQL - ORA-01403 使用“SELECT INTO”时“未找到数据”的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL ORACLE:删除时触发更新

为啥 no_data_found ORA-01403 在 Oracle 中是一个异常?

ORA-01403:未找到数据 ORA-06512:在第 8 行 01403。00000 -“未找到数据”

ORA-01403 找不到数据错误

PLSQL 触发器 ORA 01403 未找到数据

ora-01403 no data found error,ora-04088 执行触发器时出错