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