web-dev-qa-db-fra.com

Oracle PL/SQL - ORA-01403 "Aucune donnée trouvée" lors de l'utilisation de "SELECT INTO"

J'ai rencontré ce problème lors du développement d'un déclencheur dans Oracle: ORA-01403: aucune donnée trouvée . J'ai fait des recherches et j'ai compris la racine du problème. Néanmoins le traitement de l'exception d'erreur empêche l'erreur ci-dessus, mais ne résout pas mon problème.

Ce que je recherche actuellement, c’est une solution de contournement optimale pour effectuer le moins de requêtes possible et obtenir les meilleures performances possibles. Je vais essayer de décrire le scénario en créant des exemples simples à la structure réelle.

Scénario

J'ai un tableau "date référence" pour établir des périodes de temps, disons:

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)
);

Lorsque le déclencheur est déclenché, j'aurai un champ DATE - disons DATE_GIVEN (par exemple sake). Ce dont j'ai besoin c'est:

  1. Pour trouver la ligne DATE_REFERENCE dans laquelle DATE_GIVEN BETWEEN DATE_START AND DATE_END (facile); OU
  2. Si l'option précédente renvoie pas de données, je dois trouver le prochain DATE_START le plus proche à DATE_GIVEN.

Dans les deux cas, j'ai besoin de récupérer la ligne avec toutes les colonnes de la table DATE_REFERENCE, peu importe si elle correspond à Opt 1 ou 2. C'est exactement là où j'ai rencontré le problème décrit.

J'ai écrit ce bloc de test à test et essayer de trouver une solution. L'exemple ci-dessous ne fonctionne pas , je sais; mais c’est exactement ce que je veux accomplir (dans le concept). J'ai ajouté des commentaires comme -- Lots of code pour préciser que cela fera partie d'un déclencheur plus élaboré:

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;

Question

Sachant que le bloc PL/SQL ci-dessus est davantage unconceptquecode de travail, quel est le meilleur moyen de remplir RESULTROW , en tenant compte des performances et des petites requêtes possibles?

Désolé pour la longue question, mais j'ai pensé qu'une explication de scénario était nécessaire. Merci d'avance pour toute aide/pensées!

4
mathielo

Remplissez simplement le champ directement, en utilisant ordering et 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;

Cela va remplir les informations avec une requête.

MODIFIER:

Si vous voulez mettre une condition dans la sous-requête, elle doit être:

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;

Je crois que la bonne condition est DATE_GIVEN <= DATE_END. Cela couvre à la fois la condition between et devrait impliquer DATE_GIVEN < DATE_START. Cela suppose que DATE_END n'est jamais NULL.

6
Gordon Linoff

J'ai aussi eu le même problème, résolu comme ça:

si la ligne n'existe pas dans la table LADDER.INCR_PROCESS, IsPassed devient 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
Piotr Czapliński