web-dev-qa-db-ja.com

Oracle PL / SQL-「SELECT INTO」を使用すると、ORA-01403「データが見つかりません」

Oracleでトリガーを開発中にこの問題に直面しました: ORA-01403:データが見つかりません 。私はいくつかの調査を行い、問題の根本を理解しました。それでも エラー例外の処理 は上記のエラーを防ぎますが、問題を解決しません。

私が現在探しているのは、より少ないクエリ量を実行する/可能な限り最高のパフォーマンスを達成するためのoptimal回避策です。実際の構造に対する簡単な例を作成するシナリオについて説明します。

シナリオ

期間を設定するための「日付参照」テーブルがあります。

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フィールドが1つあります-DATE_GIVENと言います(例:日本酒)。私が必要なのは:

  1. DATE_REFERENCE行がDATE_GIVEN BETWEEN DATE_START AND DATE_END(簡単)である行を見つけるには、[〜#〜]または[〜#〜]
  2. 前のオプションがno dataを返す場合、DATE_STARTからDATE_GIVENに次に近いものを見つける必要があります。

どちらの場合も、オプション1または2と一致するかどうかに関係なく、テーブルDATE_REFERENCEからすべての列を含む行を取得する必要があります。ここで、説明した問題に直面しました。

このテストブロックをtestに書き込み、解決策を見つけようとしました。下の例は機能していませんです。 しかし、exactly達成したいこと(概念的に)。 -- Lots of codeなどのコメントを追加して、より複雑なトリガーの一部になることを明確にしました。

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ブロックはworking codeよりもconceptの方が多いことを知って、 RESULTROWにデータを取り込むための最良の方法は、パフォーマンスと可能な限り少ないクエリを考慮してですか?

長い質問で申し訳ありませんが、シナリオの説明が必要だと思いました。どんな助け/考えでも前もってありがとう!

5
mathielo

順序付けと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;

これにより、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 <= DATE_ENDだと思います。これはbetween条件の両方をカバーし、DATE_GIVEN < DATE_STARTを意味するはずです。これは、DATE_ENDNULLではないことを前提としています。

6
Gordon Linoff

私も同様の問題があり、次のように解決しました:

行がテーブル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;
0