[POSTGRES SQL] Besoin d'aide

Besoin d'aide [POSTGRES SQL] - SQL/NoSQL - Programmation

Marsh Posté le 30-11-2014 à 13:06:04    

Bonjour à tous,
J'aurais besoin d'aide pour une fonction que voici;
 
CREATE OR REPLACE FUNCTION MonClient_Update_Delete() RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        UPDATE Client  
   SET (NEW.idclient, NEW.nom, NEW.prenom, NEW.nom, NEW.telephone, NEW.mail, NEW.adresse, NEW.password, NEW.langue)  
   WHERE (OLD.idclient, OLD.nom, OLD.prenom, OLD.nom, OLD.telephone, OLD.mail, OLD.adresse, OLD.password, OLD.langue);
  RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        DELETE FROM Client  
   WHERE (OLD.idclient, OLD.nom, OLD.prenom, OLD.nom, OLD.telephone, OLD.mail, OLD.adresse, OLD.password, OLD.langue);
  RETURN NULL;
 END IF;
END;
$$ language plpgsql;
 
Il me met une erreur sur mon Update, j'ai cherché partout sur internet mais aucun exemple similaire.
En gros cette fonction me sert lors des Update sur la vue associée qui a les meme parametres.
 
Merci d'avance de votre aide !

Reply

Marsh Posté le 30-11-2014 à 13:06:04   

Reply

Marsh Posté le 30-11-2014 à 13:49:53    

Pareil ici :
CREATE OR REPLACE FUNCTION Check_Activite() RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN NEW;
  ELSE
   RETURN OLD;
  END IF;
    ELSIF (TG_OP = 'DELETE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN OLD;
  ELSE
   RETURN NULL;
  END IF;
 END IF;
END;
$$ language plpgsql;
 
Cette fonction est sencée me dire si l'utilisateur cherche a modifier ou supprimer ses propres données...
 
CREATE TABLE Service (
 idservice INTEGER,
 idproprio VARCHAR(20) NOT NULL,
 nom VARCHAR(50) NOT NULL,
 prix FLOAT NOT NULL check (prix>0),
 datedebut DATE NOT NULL,
 datefin DATE NOT NULL check (datedebut<datefin),
 nbpersonne INTEGER NOT NULL check (nbpersonne > 0),
 saison SAISON NOT NULL,
 image varchar(50),
 description VARCHAR(255) NOT NULL,
 ville varchar(20) NOT NULL,
 codepostal INTEGER NOT NULL,
 pays PAYS NOT NULL,
 PRIMARY KEY (idservice),
 FOREIGN KEY (ville, codepostal, pays) REFERENCES Lieu(nomville, codepostal, pays),
 FOREIGN KEY (idproprio) REFERENCES Professionnel(idclient)
);
 
CREATE TABLE Activite (
 idservice INTEGER,
 typeactivite TYPEACTIVITE NOT NULL,
 heuredebut TIME NOT NULL,
 heurefin TIME NOT NULL,
 PRIMARY KEY (idservice),
 FOREIGN KEY (idservice) REFERENCES Service(idservice)
);

Reply

Marsh Posté le 02-12-2014 à 18:32:35    

Up svp !

Reply

Marsh Posté le 04-12-2014 à 00:16:42    

wahoo, bien ce language :)
pas de besoin de définir les variables propres, ni les paramètres ...
On peut aussi lancer des ordres de créations et ou de drop d'une fonction comme ça... sur une même ligne...
A quoi correspond tes $$?  que veut dire TRIGGER $$?
 
Une question toute bete :
dans tes where de ta fonction MonClient_Update_Delete()  
tu ne mets pas les champs sur lesquels doit se faire l'update
est ce normal?
en sql "classique"  
tu ecris : update ma_table  set mon_champ = ma_valeur where mon_champ_clé = ma_valeur_clé ... (la forme simple)
 
De plus: dans ton update, tu updates ton identifiant client (généralement unique) et correspondant à une des clé ou la clé de ta table client... et ça c'est vraiment pas sur que ta base de données aime...
et même si elle aime... C'est pas bien ! On n'update jamais une clé !!!
 
en plus si ton idclient est ta clé, tu n'as besoin que de faire une clause where sur ce champ...
 
2 petites questions "complémentaires":  
Pourquoi faire une fonction? et pas une procédure stockée?  
 
Un autre truc, je vais passer pour un vieux con lol, mais une fonction ou une procédure stockée, normalement c'est "unitaire" comme action, non ?
En développement cela ne mange pas de pain et pour la maintenance c'est carrément mieux :) Dans ton programme, quand tu appelles une fonction ou une procédure stockée,  tu sais exactement si tu es en update , en insertion et ou en delete, car cela ne représente pas la même action "physiquement" ou à ton étape de process...
donc 3 fonctions (ou procédures stockées) distinctes : sans test intérieur pour savoir si tu es en update ou en delete ou en insert et donc plus rapide et après plus simple à gérer, à maintenir.
et surtout on n'update jamais une clé (ou un champ d'une clé)... :bounce:


Message édité par gpl73 le 04-12-2014 à 00:24:48

---------------
mieux vaut être un con au chaud, qu'un con gelé lol
Reply

Marsh Posté le 04-12-2014 à 16:17:43    

Les $$ sont la délimitation du corps du body !
J'ai compris pour la modification de la clé, en effet, il n'y a aucune raison de le faire cependant mon erreur reste, je ne sais pas, j'ai toujours la même erreur !
 
Je fais une fonction car c'est ce que l'on m'a appris à faire, avec ce langage, je sais qu'il en existe d'autre mais je ne me voit pas apprendre un autre langage !  
 
CREATE OR REPLACE FUNCTION Check_Activite() RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN NEW;
  ELSE
   RETURN OLD;
  END IF;
    ELSIF (TG_OP = 'DELETE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN OLD;
  ELSE
   RETURN NULL;
  END IF;
 END IF;
END;
$$ language plpgsql;
 
 
 
 
CREATE OR REPLACE FUNCTION MonClient_Update_Delete() RETURNS TRIGGER AS  
$$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        UPDATE Client  
   SET (idclient=OLD.idclient, nom=NEW.nom, prenom=NEW.prenom, telephone=NEW.telephone, mail=NEW.mail, adresse=NEW.adresse, password=NEW.password, langue=NEW.langue)  
   WHERE (idclient=OLD.idclient, nom=OLD.nom, prenom=OLD.prenom, telephone=OLD.telephone, mail=OLD.mail, adresse=OLD.adresse, password=OLD.password, langue=OLD.langue);
  RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        DELETE FROM Client  
   WHERE (idclient=OLD.idclient, nom=OLD.nom, prenom=OLD.prenom, telephone=OLD.telephone, mail=OLD.mail, adresse=OLD.adresse, password=OLD.password, langue=OLD.langue);
  RETURN NULL;
 END IF;
END;
$$  
language plpgsql;
 
DROP TRIGGER MonClient_Update_Delete ON MonClient;
CREATE TRIGGER MonClient_Update_Delete
    INSTEAD OF UPDATE OR DELETE ON MonClient
    FOR EACH ROW
    EXECUTE PROCEDURE MonClient_Update_Delete();


Message édité par MrVhek le 04-12-2014 à 16:18:13
Reply

Marsh Posté le 04-12-2014 à 17:34:25    

Surtout, SURTOUT ne dis surtout pas ce que disent les messages d'erreurs, ca rendrait les choses beaucoup trop difficiles, tout le monde sait que les messages d'erreurs c'est toujours 'Dommage gros, ca marche pas!' sans aucun indice de pourquoi ca marche pas.
 
Connais pas postgres mais deja si ca suit de loin les "standards", ca m'etonnerait que ce genre de ligne soit valide:
IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL)
 
D'une part parce qu'il est probablement syntaxiquement incorrect de mettre une requete directement dans un IF; normalement, tu dois d'abord executer ta requete, affecter le resultat a une variable et seulement ensuite faire ce que tu veux avec ta variable dans le IF. Ex:

SELECT s.idproprio  
INTO v_idproprio
FROM Service s, Activite a  
WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL
 
IF (v_idproprio IS NOT NULL) THEN ...


 
D'autre part parce que instictivement, meme si c'etait possible de requeter directement comme tu le fais, un IF ca attend une condition, autrement dit un boolean true/false. Or s.idproprio c'est pas un boolean, du coup ton IF ne veut rien dire. "IF v_idpriorio THEN something"? Peut-etre que des langages peu regardants autorisent ca et le traduisent directement en "IF v_idpriorio exists/is not null THEN something" mais bon en general moins tu laisses quelque chose a l'appreciation du compilo, mieux c'est.
 
Mais de toute maniere, les messages d'erreurs te (et nous) renseigneront mieux sur pourquoi ca marche pas.
 
Autre chose: je sais pas si c'est juste un exo, auquel cas c'est peut-etre voulu, ou si c'est pour un vrai truc qui est justifie, ou si c'est qu'un debut et tu vas les modifier/y ajouter des fonctionnalites plus tard, mais tes triggers tels qu'ils sont la, fonctionnellement, c'est de la merde en barre.
J'ai jete un coup d'oeil a la doc pour etre sur (et d'ailleurs la doc officielle est deja relativement honteuse a mon avis), et d'ailleurs j'ai peut-etre mal compris certains trucs tellement c'est mal explique, mais:
- Check_Activite() a priori ne fait rien; tu ne donnes pas a quel niveau il est declare mais il a l'air de ne servir absolument a rien. Si tu updates tu retournes new, si tu deletes tu retournes old: pas de changement par rapport au comportement sans trigger, utilite = 0 pour probablement une petite perte en perf puisque tu forces quand meme le lancement du trigger.
- MonClient_Update_Delete() c'est encore mieux: on demande une update, tu interceptes la demande, fais l'update dans le trigger, puis forwarde encore new histoire que le process normal re-update ta ligne une second fois avec les memes valeurs. Super utile pour diminuer les perfs si tu trouves que tes process sont trop rapides! D'ailleurs si tu as lu jusque-la, ca me fait penser que Oracle ne laisserait pas faire ca: par definition, tu es deja en train de modifier les lignes de ta table client, donc cette table est mutated et tu ne peux pas y toucher (et meme la lire) pendant cette operation. C'est p'tetre tout simplement ca ton probleme si Postgres prend la meme approche.
 
Bref, a+


---------------
C'était vraiment très intéressant.
Reply

Marsh Posté le 04-12-2014 à 19:34:49    

C'est étrange en effet :)
Il faut regarder le code erreur...
C'est une erreur à l'execution?
 
A mon avis cela vient surtout du fait que dans tes IF tes requetes remontent plus d'une valeur... non?
 
De plus c'est bizzard tu écris tjs ta requete avec la même "erreur"
UPDATE Client  
   SET nom=NEW.nom, prenom=NEW.prenom, telephone=NEW.telephone, mail=NEW.mail, adresse=NEW.adresse, password=NEW.password, langue=NEW.langue
   WHERE idclient=NEW.idclient;
 
de même pour ton delete...  
Delete Client where idclient=OLD.idclient;
 
Tu testes TG_OP = UPDATE ou DELETE ... pour c'est pas BFR ou AFT_UPDATE et BFR_DELETE?


Message édité par gpl73 le 04-12-2014 à 20:07:49

---------------
mieux vaut être un con au chaud, qu'un con gelé lol
Reply

Marsh Posté le 12-12-2014 à 15:57:21    

Je vais repartir du début et tenter de vous expliquer mes triggers et ce qu'ils sont censés faire.
 
Depuis le début (et pourquoi je n'ai pas mis l'erreur précise), ce ne sont que des syntaxes erreurs les problèmes viennent bien d'ici.
 
DROP TRIGGER Check_Update_Delete_Service ON Service;
CREATE TRIGGER Check_Update_Delete_Service
    BEFORE UPDATE OR DELETE ON Activite
    FOR EACH ROW
    EXECUTE PROCEDURE Check_Service();
 
CREATE OR REPLACE FUNCTION Check_Activite() RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN NEW;
  ELSE
   RETURN OLD;
  END IF;
    ELSIF (TG_OP = 'DELETE') THEN
        IF (SELECT s.idproprio FROM Service s, Activite a WHERE s.idservice=a.idservice AND a.idservice=OLD.idservice AND a.idproprio = USER IS NOT NULL) THEN
   RETURN OLD;
  ELSE
   RETURN NULL;
  END IF;
 END IF;
END;
$$ language plpgsql;

 
Commençons donc par cette fonction et son trigger associé.
Le trigger donc est un BEFORE et va lancer la fonction qui exécutera ou pas le Update ou Delete.
 
Le but de la fonction Check_Activite() est donc :
- si c'est un Update, elle va vérifier que le propriétaire du service est bien l'utilisateur connecté :
Je voudrais juste que si la requête renvoie rien, ce qui signifie que il essaye de modifier une ligne ne lui appartenant pas, cela ne fasse pas l'Update.
Sinon dans le cas où la la requête renvoie quelque chose, cela Update
 
- si c'est un Delete , de même, que cela ne valide que si la requête renvoie au moins une ligne, d'où mon IS NULL
 
CREATE TABLE Service (
 idservice INTEGER,
 idproprio VARCHAR(20) NOT NULL,
 nom VARCHAR(50) NOT NULL,
 prix FLOAT NOT NULL check (prix>0),
 datedebut DATE NOT NULL,
 datefin DATE NOT NULL check (datedebut<datefin),
 nbpersonne INTEGER NOT NULL check (nbpersonne > 0),
 saison SAISON NOT NULL,
 image varchar(50),
 description VARCHAR(255) NOT NULL,
 ville varchar(20) NOT NULL,
 codepostal INTEGER NOT NULL,
 pays PAYS NOT NULL,
 PRIMARY KEY (idservice),
 FOREIGN KEY (ville, codepostal, pays) REFERENCES Lieu(nomville, codepostal, pays),
 FOREIGN KEY (idproprio) REFERENCES Professionnel(idclient)
);
 
CREATE TABLE Activite (
 idservice INTEGER,
 typeactivite TYPEACTIVITE NOT NULL,
 heuredebut TIME NOT NULL,
 heurefin TIME NOT NULL,
 PRIMARY KEY (idservice),
 FOREIGN KEY (idservice) REFERENCES Service(idservice)
);

 
Voici mes deux tables Activite et Service.


Message édité par MrVhek le 12-12-2014 à 16:00:50
Reply

Marsh Posté le 12-12-2014 à 16:04:46    

Maintenant passons à la deuxième fonction et son trigger associé.

CREATE OR REPLACE FUNCTION MonClient_Update_Delete() RETURNS TRIGGER AS  
$$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        UPDATE Client  
   SET (idclient=OLD.idclient, nom=NEW.nom, prenom=NEW.prenom, telephone=NEW.telephone, mail=NEW.mail, adresse=NEW.adresse, password=NEW.password, langue=NEW.langue)  
   WHERE idclient=NEW.idclient;
  RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        DELETE FROM Client  
   idclient=OLD.idclient;  
  RETURN NULL;
 END IF;
END;
$$  
language plpgsql;
 
DROP TRIGGER MonClient_Update_Delete ON MonClient;
CREATE TRIGGER MonClient_Update_Delete
    INSTEAD OF UPDATE OR DELETE ON MonClient
    FOR EACH ROW
    EXECUTE PROCEDURE MonClient_Update_Delete();

 
Ce trigger va s'executer lorsque la personne va essayer de faire un Update ou un Delete sur ma vue qui s'appelle MonClient
 
CREATE TABLE Client (
 idclient VARCHAR(20),
 nom VARCHAR(20) NOT NULL,
 prenom VARCHAR(20) NOT NULL,
 telephone NUMERIC(10) NOT NULL,
 mail VARCHAR(40) NOT NULL,
 adresse VARCHAR(50) NOT NULL,
 password VARCHAR(64) NOT NULL,
 langue LANGUE NOT NULL,
 nomville VARCHAR(20),
 codepostal INTEGER NOT NULL,
 pays PAYS NOT NULL,
 PRIMARY KEY (idclient),
);
 
CREATE VIEW MonClient as
 SELECT *
 FROM Client
 WHERE idclient=USER;

 
Ainsi le but de cette fonction est juste d'Update ou de Delete dans la table client plutôt que dans la vue (ce qui est impossible).
 
Merci d'avance pour votre aide !


Message édité par MrVhek le 12-12-2014 à 16:10:33
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed