web-dev-qa-db-fra.com

Moyen correct de vérifier si une ligne existe dans la table [pl/sql block]

J'écrivais des tâches hier et je suis frappé par le fait que je ne sais pas vraiment de manière générale de vérifier si une ligne existe dans la table lorsque j'utilise pl/sql. 

Par exemple, utilisons la table 

PERSON(ID, Name); 

Evidemment je ne peux pas faire (sauf s'il existe une méthode secrète) quelque chose comme:

BEGIN 
  IF EXISTS SELECT id FROM person WHERE ID = 10; 
    -- do things when exists
  ELSE
    -- do things when doesn't exist
  END IF;
END;

Donc, ma façon habituelle de le résoudre était:

DECLARE
  tmp NUMBER;
BEGIN 
  SELECT id INTO tmp FROM person WHERE id = 10; 
  --do things when record exists
EXCEPTION
  WHEN no_data_found THEN
  --do things when record doesn't exist
END; 

Cependant, je ne sais pas si c'est un moyen accepté, ou s'il existe un meilleur moyen de vérifier, j'apprécierais vraiment que quelqu'un puisse partager sa sagesse avec moi :) 

À votre santé.

31
devBem

Je ne voudrais pas pousser le code normal dans un bloc d'exception. Il suffit de vérifier s'il existe des lignes satisfaisant votre condition et de continuer à partir de là:

declare
  any_rows_found number;
begin
  select count(*)
  into   any_rows_found
  from   my_table
  where  rownum = 1 and
         ... other conditions ...

  if any_rows_found = 1 then
    ...
  else
    ...
  end if;
53
David Aldridge

Le code IMO avec un SELECT autonome utilisé pour vérifier si une ligne existe dans une table ne tire pas pleinement parti de la base de données. Dans votre exemple, vous avez une valeur d'identification codée en dur, mais ce n'est pas ainsi que les applications fonctionnent dans "le monde réel" (du moins pas dans mon monde - le vôtre peut être différent :-). Dans une application type, vous allez utiliser un curseur pour rechercher des données. Supposons donc qu'une application examine les données de facturation et doit savoir si le client existe. Le corps principal de l'application pourrait être quelque chose comme

FOR aRow IN (SELECT * FROM INVOICES WHERE DUE_DATE < TRUNC(SYSDATE)-60)
LOOP
  -- do something here
END LOOP;

et dans le -- do something here vous voulez savoir si le client existe, et sinon imprimer un message d'erreur.

Une façon de le faire serait de mettre dans une sorte de singleton SELECT, comme dans

-- Check to see if the customer exists in PERSON

BEGIN
  SELECT 'TRUE'
    INTO strCustomer_exists
    FROM PERSON
    WHERE PERSON_ID = aRow.CUSTOMER_ID;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    strCustomer_exists := 'FALSE';
END;

IF strCustomer_exists = 'FALSE' THEN
  DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
END IF;

mais IMO, cela est relativement lent et sujet aux erreurs. IMO a Better Way (tm) consiste à l’intégrer dans le curseur principal:

FOR aRow IN (SELECT i.*, p.ID AS PERSON_ID
               FROM INVOICES i
               LEFT OUTER JOIN PERSON p
                 ON (p.ID = i.CUSTOMER_PERSON_ID)
               WHERE DUE_DATA < TRUNC(SYSDATE)-60)
LOOP
  -- Check to see if the customer exists in PERSON

  IF aRow.PERSON_ID IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
  END IF;
END LOOP;

Ce code compte sur PERSON.ID étant déclaré comme clé primaire sur la personne (ou au moins comme étant non nul); la logique est que si la table PERSON est jointe de manière externe à la requête et que PERSON_ID est défini comme NULL, cela signifie qu'aucune ligne n'a été trouvée dans PERSON pour le CUSTOMER_ID donné, car PERSON.ID doit avoir une valeur NUL).

Partager et profiter.

7
Bob Jarvis

De nombreuses façons de peau ce chat. Je mets une fonction simple dans le paquet de chaque table ...

function exists( id_in in yourTable.id%type ) return boolean is
  res boolean := false;
begin
  for c1 in ( select 1 from yourTable where id = id_in and rownum = 1 ) loop
    res := true;
    exit; -- only care about one record, so exit.
  end loop;
  return( res );
end exists;

Rend vos chèques vraiment propres ...

IF pkg.exists(someId) THEN
...
ELSE
...
END IF;
5
gfrobenius
select nvl(max(1), 0) from mytable;

Cette instruction donne 0 s'il n'y a pas de lignes, 1 si vous avez au moins une ligne dans cette table. C'est beaucoup plus rapide que de faire un compte choisi (*). L'optimiseur "voit" qu'il suffit d'extraire une seule ligne pour répondre à la question.

Voici un petit exemple (détaillé):

declare
  YES constant      signtype := 1;
  NO  constant      signtype := 0;
  v_table_has_rows  signtype;
begin

  select nvl(max(YES), NO)
    into v_table_has_rows
    from mytable -- where ...
  ;

  if v_table_has_rows = YES then
    DBMS_OUTPUT.PUT_LINE ('mytable has at least one row');
  end if;

end;
4
Andreas Wismann
select max( 1 )
  into my_if_has_data
  from MY_TABLE    X
 where X.my_field   = my_condition
   and rownum       = 1;

Ne pas parcourir tous les enregistrements.

Si MY_TABLE n'a pas de données, alors my_if_has_data est défini sur null.

0
Robert Tebiev

Si vous utilisez un curseur explicite, procédez comme suit.

DECLARE
   CURSOR get_id IS 
    SELECT id 
      FROM person 
      WHERE id = 10;

  id_value_ person.id%ROWTYPE;
BEGIN 
   OPEN get_id;
   FETCH get_id INTO id_value_;

   IF (get_id%FOUND) THEN
     DBMS_OUTPUT.PUT_LINE('Record Found.');
   ELSE
     DBMS_OUTPUT.PUT_LINE('Record Not Found.');
   END IF;
   CLOSE get_id;

EXCEPTION
  WHEN no_data_found THEN
  --do things when record doesn't exist
END;
0
Dinidu Hewage
Select 'YOU WILL SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 1);

Select 'YOU CAN NOT SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 0);

Select 'YOU WILL SEE ME, TOO' as ANSWER from dual
where not exists (select 1 from dual where 1 = 0);
0
Mohsen Heydari