Trigger Oracle : la fonction renvoie NULL

Fermé
Kyra - 6 août 2008 à 00:57
 kyra - 8 août 2008 à 19:11
Bonjour,

Mon trigger utilise une fonction stockée dans un package, mais cette fonction (qui fonctionne parfaitement sous SQL+ ou Windev...) ne renvoie rien.

Il n'y a pas de commit/rollback dans la fonction, pas d'erreur de paramètres (j'ai même essayé avec les valeurs en dur, copié/collé de la requête lancée sous SQL+ et inversement);

Que j'affecte le résultat à une variable : Var := Schema.pkg.fct(parm);
ou via SQL : SELECT Schema.pkg.fct(parm) INTO Var FROM DUAL;
ou directement dans la requête de création : INSERT INTO Fichier value(Schema.pkg.fct(parm));
avec ou sans le nom du schéma, je n'ai jamais de résultat.

Je ne pense pas qu'il y ait d'erreur dans mon code : tout le reste est OK, sauf cette valeur qui est nulle (ou = 999 si je mets un NVL(Schema.pkg.fct(parm),999)

Quelqu'un peut-il m'aider, SVP ? Merci d'avance (ça fait plusieurs jours que je galère là-dessus)

5 réponses

sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
6 août 2008 à 13:10
Salut,

Si tu peux poster ton code (élagué, avec uniquement le nécessaire pour montrer le souci), ça aiderait beaucoup.
++
0
Bonjour et merci de vous préoccuper de mon problème.
Mes deniers tests : avec 'execute immediate' Select avec variables & Select avec constantes... pas mieux
Le problème est sur 'val_pmp' qui est recherché au début du code; au départ, je le testais 'IF val_pmp > 0' avant de continuer et il ne se passait jamais rien puisqu'aucune valeur n'est renvoyée.
Le select ... from dual renvoie la valeur 0.221 sous SQL

Voici mon code :
CREATE OR REPLACE TRIGGER ETHIC.GRESSET_STOCKMATIERES_FR_PMP
AFTER INSERT or UPDATE or DELETE ON ETHIC.MVTSPHYSSTOCKMATIERES FOR EACH ROW
WHEN (NEW.TYPEMVT='ER') -- Si mvt de type Réception Automatique d'une Commande Fournisseur => 'ER'
Declare
val_pmp NUMBER;
fr_pmp NUMBER := 41023; --Id du fournisseur "PMP"
fr_pmp_existe NUMBER;
unite_prix_achat VARCHAR2(3) := '';
---------------------------------------------------------------------------------------------
-- AJ 2008-07-29 : A chaque réception de commande d'achat (Mvt Physique de matière de type 'ER')
-- Création / màj d'une ligne dans le fichier des fournisseurs de l'article,
-- avec prix = PMP (calculé dans l'unité d'achat de l'article)
BEGIN
execute immediate 'SELECT ETHIC.PKG1.PMP(''GGR'',3021) INTO val_pmp FROM Dual';
-- execute immediate 'SELECT ETHIC.PKG1.PMP(:NEW.CODESOCIETE,:NEW.IDREFSTOCK) INTO val_pmp FROM Dual';
-- val_pmp := ETHIC.PKG1.PMP(:NEW.CODESOCIETE,:NEW.IDREFSTOCK);

-- Recherche de l'unité du PMP = unité d'achat de la fiche article
Select CODEUNITEPRIXACHAT INTO unite_prix_achat FROM ETHIC.PSTOCKMATIERESENTETE where CODESOCIETE=:NEW.CODESOCIETE and IDREFSTOCK=:NEW.IDREFSTOCK;

-- SUPPRESSION du fournisseur 'PMP' si existe déjà pour cet article
BEGIN
DELETE FROM ETHIC.PSTOCKMATIERESFOURNISSEURS WHERE CODESOCIETE=:NEW.CODESOCIETE and IDREFSTOCK=:NEW.IDREFSTOCK and NOCOMPTE= fr_pmp;
EXCEPTION
WHEN OTHERS THEN NULL;
END;

-- [RE]CREATION de la ligne fournisseur "PMP"
BEGIN
Insert into ETHIC.PSTOCKMATIERESFOURNISSEURS values(:NEW.CODESOCIETE, 'GRE', :NEW.IDREFSTOCK, :NEW.CODECLASSE, 0, 0, 'F', fr_pmp, 'pmp', 0,nvl(val_pmp,888), unite_prix_achat, 'Valeur du PMP au ' || to_char(sysdate,'dd/mm/yyyy')) ;
EXCEPTION
WHEN OTHERS THEN NULL;
END;


EXCEPTION
WHEN OTHERS THEN NULL;
END;
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
6 août 2008 à 18:04
OK... Il me semble que si tu vx retourner des données via la SP, tu ne peux pas le faire directement...
Il existerait deux solutions:
1. au lieu de passer par la SP, tu fais ton SELECT directement dans le trigger
2. si tu veux conserver ta SP, il faudra la modifier pour qu'elle possède un paramètre OUT de type sys_refcursor. Plsu d'infos sur le maniement des sys_refcursor dans les SP ici: http://www.digcode.com/...

++
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
6 août 2008 à 18:17
Exemple:
CREATE TABLE a (b INT, c VARCHAR2(10));
CREATE  TABLE b (ID INT, country VARCHAR2(10));

CREATE OR REPLACE PROCEDURE getcountry (iid IN INT, resultset OUT sys_refcursor)
AS
BEGIN
   OPEN resultset FOR
      SELECT country
        FROM b
       WHERE ID = iid;
END getcountry;

insert into b values (1, 'france');
insert into b values (2, 'usa');
commit;

CREATE OR REPLACE TRIGGER dispnew.a_trg
   AFTER INSERT OR UPDATE OR DELETE
   ON dispnew.a
   FOR EACH ROW
   WHEN (NEW.c = 'ER')
DECLARE
   val_pmp           VARCHAR2 (10);
   val_pmp_rc        sys_refcursor;
   val_pmp_country   a.c%TYPE;
---------------------------------------------------------------------------------------------
-- AJ 2008-07-29 : A chaque réception de commande d'achat (Mvt Physique de matière de type 'ER')
-- Création / màj d'une ligne dans le fichier des fournisseurs de l'article,
-- avec prix = PMP (calculé dans l'unité d'achat de l'article)
BEGIN
   getcountry (1, val_pmp_rc);

   LOOP
      FETCH val_pmp_rc
       INTO val_pmp_country;

      EXIT WHEN val_pmp_rc%NOTFOUND;
      DBMS_OUTPUT.put_line ('val_pmp=' || val_pmp_country);
   END LOOP;

   CLOSE val_pmp_rc;
EXCEPTION
   WHEN OTHERS
   THEN
      NULL;
END;

insert into a (b, c) values (2, 'ER');


==> output sous Toad = val_pmp=france

Bon, il est vrai que je me suis égaré de ton code en simplifiant la procédure, mais tu as là qqch qui marche: trigger avec appel de SP.
0
kyra > sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010
6 août 2008 à 19:36
J'ai eu un peu de mal à saisir ce qu'était une SP au départ; puis j'ai essayé une variante de ce que vous proposez; mais ça ne marche pas mieux.
Promis : demain je fais une SP qui appelera ma fonction (mais j'ai quand même des doutes)
Question subsidiaire : DBMS_OUTPUT.put_line s'affiche où ? J'ai beau mettre SET SERVEROUTPUT ON ou dbms_output.enable(buffer_size => NULL); il ne se passe rien...


CREATE OR REPLACE TRIGGER ETHIC.GRESSET_STOCKMATIERES_FR_PMP
AFTER INSERT or UPDATE or DELETE ON ETHIC.MVTSPHYSSTOCKMATIERES FOR EACH ROW
WHEN (NEW.TYPEMVT='ER') -- Si mvt de type Réception Automatique d'une Commande Fournisseur => 'ER'

Declare
val_pmp NUMBER;
fr_pmp NUMBER := 41023; --Id du fournisseur "PMP"
unite_prix_achat VARCHAR2(3) := '';
val_pmp_rc sys_refcursor;
---------------------------------------------------------------------------------------------

-- AJ 2008-07-29 : A chaque réception de commande d'achat (Mvt Physique de matière de type 'ER')
-- Création / màj d'une ligne dans le fichier des fournisseurs de l'article,
-- avec prix = PMP (calculé dans l'unité d'achat de l'article)

BEGIN
BEGIN
OPEN val_pmp_rc FOR
SELECT ETHIC.PKG1.PMP(:NEW.CODESOCIETE,:NEW.IDREFSTOCK) FROM Dual;
END;
LOOP
FETCH val_pmp_rc INTO val_pmp;
EXIT WHEN val_pmp_rc%NOTFOUND;
DBMS_OUTPUT.put_line ('val_pmp=' || val_pmp);
END LOOP;

Insert into ETHIC.w_PSTOCKMATIERESFOURNISSEURS values(:NEW.CODESOCIETE, 'GRE', :NEW.IDREFSTOCK, :NEW.CODECLASSE, 0, 0, 'F', fr_pmp,
'pmp', 0,val_pmp, unite_prix_achat, 'Valeur du PMP au ' || to_char(sysdate,'dd/mm/yyyy')) ;
END;
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723 > kyra
6 août 2008 à 19:46
demain je fais une SP qui appelera ma fonction <== je crois su'il est possible de retourner le sys_refcursor par une fonction, cf. la 2ème page du lien:

CREATE OR REPLACE FUNCTION get_emp_by_dept (i_deptno emp.deptno%TYPE)
RETURN SYS_REFCURSOR
IS
    emp_refcur      SYS_REFCURSOR;
BEGIN
    OPEN emp_refcur FOR SELECT empno, ename FROM emp WHERE deptno = i_deptno;
    RETURN emp_refcur;
END;

 

The following PL/SQL statement is used to harness the above Oracle function

DECLARE
  deptno        emp.deptno%TYPE;
  empno         emp.empno%TYPE;
  ename         emp.ename%TYPE;
  emp_refcur    SYS_REFCURSOR;
BEGIN
    emp_refcur := get_emp_by_dept(deptno);
    DBMS_OUTPUT.PUT_LINE('EMPNO    ENAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    LOOP
        FETCH emp_refcur INTO empno, ename;
        EXIT WHEN emp_refcur%NOTFOUND;
        DBMS_OUTPUT.PUT_LINE(empno || '     ' || ename);
    END LOOP;
    CLOSE emp_refcur;
END;


Du coup, si tu as fait ta modif dans la fonction (n'oublie pas de rajouter le return du curseur), il faudra changer cette ligne:
SELECT ETHIC.PKG1.PMP(:NEW.CODESOCIETE,:NEW.IDREFSTOCK) FROM Dual; 

en:
val_pmp_rc := ETHIC.PKG1.PMP(:NEW.CODESOCIETE,:NEW.IDREFSTOCK) FROM Dual; 

...

DBMS_OUTPUT.put_line s'affiche où ? J'ai beau mettre SET SERVEROUTPUT ON ==> probablement il ne passe pas par là... Enfin, sous Toad je vois l'output dans un onglet après avoir fait un enable de l'output qui correspond en gros à un SET SERVEROUTPUT ON

++
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723 > kyra
6 août 2008 à 19:49
Oups, erreur:

Pas de "FROM Dual" dans l'appel de la fonction ;-)
Malheureux copier-coller, damn...
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
7 août 2008 à 10:06
Erf, après réflexion:

Il faut savoir ce qu'il y a comme code dans ta fonction. Car si tu retournes simplement une valeur (pas un ensemble de valeurs, comme avec un select), passer par un sys_refcursor est inutile. Il suffit de mettre
val_pmp := ETHIC.PKG1.PMP(''GGR'',3021) ;
dans le trigger pour avoir un fonctionnement correct.

Maintenant, si ta fonction utilise ta table sur laquelle le trigger est rattaché, il se peut qu'il y ait des restrictions et ça pourrait être la cause du dysfonctionnement.

Donc, en conclusion:
1. Vérifie si ta fonction retourne une seule valeur de type NUMBER
2. Si c'est le cas, pas besoin de sys_refcursor. Vérifie si la table MVTSPHYSSTOCKMATIERES est utilisée dans la fonction et comment
3. Si pas une valeur de retournée, mais un ensemble de valeurs (comme avec un SELECT), passer par sys_refcursor est une bonne idée (cf. mes posts 6 et 7)

Vouilou,
++
0
Bonjour !
Excuse-moi de ne pas avoir répondu hier; j'ai dû oublier de cocher "réponse par mail"... j'avais regardé sur le site le matin, mais sûrement avant ta réponse.
1) Je ne sais pas si tu as lu mon post n° 8 (plus haut) ; mais j'ai essayé de faire un test avec une petite fonction qui retrourne 1 valeur à partir d'un simple SQL sur un autre fichier... et j'ai un autre soucis : bien que ma nouvelle fonction soit correctement créée dans mon package, quand je l'utilise dans mon trigger, il me dit qu'il faut déclarer le composant "nom_new_fonction".
- De quoi cela vient-il ?

2) Il n'y a pas de problème de valeur de retour dans la fonction PMP : 1 seule valeur numérique; mais elle utilise effectivement le fichier sur lequel porte mon trigger !!! j'ai déjà eu ce pb pour un autre trigger livré par mon fournisseur : table "muttante"...
Pourtant, je me place AFTER l'opération et la fonction PMP ne fait que des lectures.
- Est-ce que ça peut être le problème ???
- Si j'arrive déjà à faire mon test avec ma "fonction simple", ça me confirmera peut-être le pb ?

... en tous cas, merci beaucoup de m'aider...
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
8 août 2008 à 19:04
Hi there!

Concernant ta 1ère remarque: j'avais fait les tests suivants:
1. création des tables a et b
2. création trigger sur a appelant successivement
3a. une procédure stockée faisant u nselect from b et avec un paramètre OUT de type sys_refcursor
3b. même procédure stockée mais "dummy" (paramètre OUT retournant un varchar2)
3c. une fonction retournant un sys_refcursor (select from b)
3d. même fonction, modifiée pour retourner un varchar2 (dummy)

Pas de souci pour les 4 tests. Il paraît donc que le souci se situe dans l'emploi de la table liée au trigger dans la SP/fonction. Et ceci même si la SP/fonction utilise la table uniquement en lecture... (la table est en cours d'être modifiée et le trigger est en cours d'exécution). Je ferai des tests dès que possible (ai été très chargé today.... sigh...), mais entre temps regarde par ici: https://www.experts-exchange.com/questions/20663278/CALLING-A-PROCEDURE-FUNCTION-IN-A-TRIGGER-IN-DATABASE-SCHEMA.html

(fais un scroll pour voir le bas de cette page, en dépit du "All comments and solutions are available to Premium Service Members only.") ;-)

A plucheu
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Gagné !

Il semble bien que le problème vienne de l'accès à la même table; j'ai fait un test en faisant un trigger qui calcul mon PMP, mais déclenché sur un autre fichier : ça marche...

Cela signifie que je ne peux pas faire mon trigger...

Que penses-tu d'un trigger "INSTEAD OF" sur la table A dans lequel je rechercherais mon PMP pour alimenter ma table B et prendrais en charge les INSERT, UPDATE ou DELETE sur la table A ?
Aurai-je encore le probléme de la table muttante ?
Y a-t-il un risque, compte tenu que je ne suis pas maître du logiciel ? Je suppose que oui, si la table évolue...

Il faut donc que je trouve une autre table sur laquelle placer mon trigger : j'ai peut-être une idée, mais la mise à jour ne sera pas visible pas l'utilisateur et ça m'ennuie...

Bon, je vais cogiter ça !
Merci de me dire ce que tu penses de mes conclusions.

@+
0
sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010 723
8 août 2008 à 19:05
Lol, 'zavons posté presque en même temps ;)
0
kyra > sandul Messages postés 3927 Date d'inscription jeudi 22 mai 2008 Statut Membre Dernière intervention 8 octobre 2010
8 août 2008 à 19:11
Merci Sandul !!

Là, je vais rentrer dans mon home-sweet-home et je ne sais pas si j'aurai le temps de revenir sur le forum.
... sinon à lunid, peut-être ?
0