Oracle Ref Cursor Vs Select into avec gestion des exceptions
Question
J'ai quelques scénarios:
-
Besoin de lire la valeur d'une colonne de trois tables différentes dans un ordre prédéfini et une seule table aura les données
-
Lire les données de la table 1 si des enregistrements sont présents pour les critères, sinon lire les données de la Table2 pour les critères donnés
Dans les procédures stockées Oracle
Pour les traiter maintenant, il faut d'abord obtenir le nombre d'une requête donnée dans une variable, et si le nombre > 0, nous exécutons la même requête pour lire les données réelles comme dans:
select count(*) from table1 into v_count
if v_count > 0
then
select data into v_data from table1
end if;
Return v_data
Ceci est fait pour éviter l'exception no_data_found, sinon il me faudrait trois blocs de gestionnaire d'exception pour intercepter l'exception no_data_found pour chaque accès à une table.
Actuellement, je suis en train de réimplémenter cela avec des curseurs afin d'avoir quelque chose comme ça:
cursor C1 is
select data from table1;
Open C1
Fetch C1 into v_data
if C1%FOUND
then
Close C1
Return v_data
End If
Je voulais savoir lequel est le meilleur du point de vue des performances: celui avec Curseurs ou celui qui convertit une variable en variable avec trois blocs d'exception no_data_found. Je ne veux pas utiliser le processus de requête en deux étapes que nous avons actuellement.
La solution
Je ne sais pas pourquoi vous êtes si désireux d'éviter l'exception? Quel est le problème avec:
begin
begin
select data into v_data from table1;
exception
when no_data_found then
begin
select data into v_data from table2;
exception
when no_data_found then
begin
select data into v_data from table3;
exception
when no_data_found then
v_data := null;
end;
end;
end;
return v_data;
end;
Je pense que cela fonctionnera mieux que votre autre solution car elle fait le minimum de travail possible pour atteindre le résultat souhaité.
Voir Comment ignorer l'exception Oracle DUP_VAL_ON_INDEX? où I démontrer que l'utilisation des exceptions est plus efficace que le comptage pour voir s'il existe des données.
Autres conseils
select count(*) from table1 into v_count
if v_count > 0 then
select data into v_data from table1;
else
v_data := null;
end if;
return v_data;
n'est pas équivalent à
begin
select data into v_data from table1;
return v_data;
exception
when no_data_found then
return null;
end;
dans un environnement multi-utilisateur. Dans le premier cas, quelqu'un pourrait mettre à jour la table entre les points où vous vérifiez l'existence et lorsque vous lisez les données.
En ce qui concerne les performances, je ne sais pas ce qui est préférable, mais je sais que la première option permet d'effectuer deux commutations de contexte vers le moteur SQL et que la seconde ne modifie qu'un contexte.
La façon dont vous gérez le scénario 1 n’est pas bonne. Non seulement vous faites deux requêtes quand une seule suffit, mais, comme le souligne Erik, cela ouvre la possibilité de données échangées entre les deux requêtes (sauf si vous utilisez une transaction en lecture seule ou sérialisable).
Etant donné que vous dites que dans ce cas, les données figureront exactement dans l'un des trois tableaux, qu'en est-il de cela?
SELECT data
INTO v_data FROM
(SELECT data FROM table1
UNION ALL
SELECT data FROM table2
UNION ALL
SELECT data FROM table3
)
Une autre "astuce" vous pouvez utiliser pour éviter d'écrire plusieurs gestionnaires sans données trouvées:
SELECT MIN(data) INTO v_data FROM table1;
IF v_data IS NOT NULL THEN
return v_data;
END IF;
SELECT MIN(data) INTO v_data FROM table2;
...etc...
mais je ne vois vraiment aucune raison qui soit meilleure que d'avoir trois gestionnaires d'exception.
Pour votre deuxième scénario, je pense que ce que vous voulez dire, c'est qu'il peut y avoir des données dans les deux tables et que vous souhaitez utiliser les données de table1, le cas échéant, sinon utilisez les données de la table 2. Encore une fois, vous pouvez le faire en une seule fois. requête:
SELECT data
INTO v_data FROM
(SELECT data FROM
(SELECT 1 sort_key, data FROM table1
UNION ALL
SELECT 2 sort_key, data FROM table2
)
ORDER BY sort_key ASC
)
WHERE ROWNUM = 1
Une version améliorée de l'option MIN de "Dave Costa" ...
SELECT COUNT(1), MIN(data) INTO v_rowcount, v_data FROM table2;
Maintenant, v_rowcount
peut être vérifié pour les valeurs 0, > 1 (supérieures à 1) où la requête de sélection normale renvoie NO_DATA_FOUND
ou TOO_MANY_ROWS
exception. Valeur " 1 " indiquera qu’une seule ligne existe et servira notre objectif.
DECLARE
A VARCHAR(35);
B VARCHAR(35);
BEGIN
WITH t AS
(SELECT OM_MARCA, MAGAZIA FROM ifsapp.AKER_EFECTE_STOC WHERE (BARCODE = 1000000491009))
SELECT
(SELECT OM_MARCA FROM t) OM_MARCA,
(SELECT MAGAZIA FROM t) MAGAZIA
INTO A, B
FROM DUAL;
IF A IS NULL THEN
dbms_output.put_line('A este null');
END IF;
dbms_output.put_line(A);
dbms_output.put_line(B);
END;
/
Utilisez le " pour la ligne dans le curseur " forme d'une boucle et la boucle ne sera tout simplement pas traitée s'il n'y a pas de données:
declare cursor
t1Cur is
select ... from table1;
t2Cur is
select ... from table2;
t3Cur is
select ... from table3;
t1Flag boolean FALSE;
t2Flag boolean FALSE;
t3Flag boolean FALSE;
begin
for t1Row in t1Cur loop
... processing, set t1Flag = TRUE
end loop;
for t2Row in t2Cur loop
... processing, set t2Flag = TRUE
end loop;
for t3Row in t3Cur loop
... processing, set t3Flag = TRUE
end loop;
... conditional processing based on flags
end;