OVH Cloud OVH Cloud

Activation multiple de TRIGGER

3 réponses
Avatar
Arnaud CAVELIER
Bonjour,

J'espère que vous allez pouvoir m'aider car je commence à vraiment tourner
en rond :

Dans mon Trigger, je souhaite comparer 3 valeurs de la table INSERTED à 3
trois valeurs lues dans une autre table pour valider la modification de la
ligne :

Si 1 des 3 valeurs n'est pas identique, je dois annuler la mise à jour
(Rollback).

Voici un peu de code pour être plus clair :
ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
FOR UPDATE
AS
DECLARE @StationMaitre nvarchar(50)
DECLARE @TypeVerrouMaitre tinyint
DECLARE @OperateurMaitre int
DECLARE @Station nvarchar(50)
DECLARE @TypeVerrou tinyint
DECLARE @Operateur int
DECLARE @CptExpedition int
DECLARE @Message nvarchar(4000)
SELECT
@CptExpedition=CptExpedition,@Station=Station,@TypeVerrou=TypeVerrou,@Operateur=Operateur FROM inserted
SELECT
@StationMaitre=Station,@TypeVerrouMaitre=TypeVerrou,@OperateurMaitre=Operateur FROM VERROU WHERE RefCpt=@CptExpedition
Select @Message=' MAJ Exped '+ convert(nvarchar,@CptExpedition) + ' St=' +
convert(nvarchar,@Station) + '=' + convert(nvarchar,@StationMaitre) +'
Op='+convert(nvarchar,@Operateur)+'='+convert(nvarchar,@OperateurMaitre)+'
TypVer=' + convert(nvarchar,@TypeVerrou) + '=' +
convert(nvarchar,@TypeVerrouMaitre)
insert debug (msg) values (@Message)
IF (isnull(len(@StationMaitre),0)=0) AND (isnull(@TypeVerrouMaitre,0)=0) AND
(isnull(@OperateurMaitre,0)=0) --@@ROWCOUNT=1
BEGIN
insert debug (msg) values('RAISERROR('' ROLLBACK Pas de verrou'' , 16, 1)
ROLLBACK TRANSACTION')
END
ELSE
BEGIN
IF (@StationMaitre=@Station) AND (@TypeVerrouMaitre=@TypeVerrou) AND
(@OperateurMaitre=@Operateur)
BEGIN
insert debug (msg) values ('OK')
END
ELSE
BEGIN
insert debug (msg) values ('RAISERROR('' ROLLBACK Mauvais operateur'', 16,
1 )
ROLLBACK TRANSACTION')
END
END

GO

La table debug me permet juste de "voir" le résultat théorique du trigger.
En prod, j'enlèverai l'insert debug pour ne laisser que le contenu de la
valeur ajoutée.

Le problème est que pour modifier 1 seul champ dans la table, j'ai
l'impression que le trigger est activé 3 fois : 2 fois en passant par PAS DE
VERROU et la dernière fois par OK
En production cela ne fonctionnera pas car le Rollback annulera la
transaction avant d'être arrivé à terme.

Je n'arrive pas à comprendre pourquoi il y a des activations qui ne
correspondent pas à ce que je souhaiterai !


Merci de votre aide.
ac at expedito dot fr

3 réponses

Avatar
Fred BROUARD
Un trigger ne fonctionne pas comme tu le souhaite ! Il fonctionne comme il doit
fonctionner...

Un trigger voit passer d'un seul coup TOUTES les modifs de TOUS les tuples en jeu.

Ainsi, lors de l'exécution de l'ordre :

UPDATE EXPEDITION
SET Operateur = UPPER(Operateur)

Toutes les lignes de la table seront modifiées.
Ce qui veur dire que la pseudo table inserted contiendra autant de lignes qu'il
y a de lignes dans la table EXPEDITION.

Donc ton code est déjà anormal puisqu'il présuppose que inserted ne contient
jamais qu'une seule ligne !

en effet, a quelle ligne se réfère :
SELECT @CptExpedition=CptExpedition,
@Station=Station,
@TypeVerrou=TypeVerrou,
@Operateur=Operateur
FROM inserted
???

Pour gérer les effets d'un triggers, il faut, soit gérer le contrôle de manière
ensembliste (requête avec intégration de la table inserted liée aux tables en
jeu), soit utiliser un curseur (beaucoup moins performant, mais nécessaire
lorque le traitement est "au cas par cas").

En l'occurence et à mon avis, ton trigger peut se résumer à :

ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
FOR UPDATE
AS

IF EXISTS (SELECT *
FROM inserted i
INNER JOIN VERROU v
ON i.CptExpedition = v.RefCpt
WHERE i.Station <> v.Station
OR i.TypeVerrou <> v.TypeVerrou
OR i.Operateur <> v.Operateur)
ROLLBACK TRANSACTION
GO

C'est tout !

En régle générale, la présence de variable dans un trigger est un bon indice
pour montrer que quelque chose ne va pas !

A +

--
Frédéric BROUARD, MVP SQL Server. Expert SQL / spécialiste Delphi, web
Livre SQL - col. Référence : http://sqlpro.developpez.com/bookSQL.html
Le site du SQL, pour débutants et pros : http://sqlpro.developpez.com
************************ www.datasapiens.com *************************



Arnaud CAVELIER a écrit:
Bonjour,

J'espère que vous allez pouvoir m'aider car je commence à vraiment tourner
en rond :

Dans mon Trigger, je souhaite comparer 3 valeurs de la table INSERTED à 3
trois valeurs lues dans une autre table pour valider la modification de la
ligne :

Si 1 des 3 valeurs n'est pas identique, je dois annuler la mise à jour
(Rollback).

Voici un peu de code pour être plus clair :
ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
FOR UPDATE
AS
DECLARE @StationMaitre nvarchar(50)
DECLARE @TypeVerrouMaitre tinyint
DECLARE @OperateurMaitre int
DECLARE @Station nvarchar(50)
DECLARE @TypeVerrou tinyint
DECLARE @Operateur int
DECLARE @CptExpedition int
DECLARE @Message nvarchar(4000)
SELECT
@CptExpedition=CptExpedition,@Station=Station,@TypeVerrou=TypeVerrou,@Operateur=Operateur FROM inserted
SELECT
@StationMaitre=Station,@TypeVerrouMaitre=TypeVerrou,@OperateurMaitre=Operateur FROM VERROU WHERE RefCpt=@CptExpedition
Select @Message=' MAJ Exped '+ convert(nvarchar,@CptExpedition) + ' St=' +
convert(nvarchar,@Station) + '=' + convert(nvarchar,@StationMaitre) +'
Op='+convert(nvarchar,@Operateur)+'='+convert(nvarchar,@OperateurMaitre)+'
TypVer=' + convert(nvarchar,@TypeVerrou) + '=' +
convert(nvarchar,@TypeVerrouMaitre)
insert debug (msg) values (@Message)
IF (isnull(len(@StationMaitre),0)=0) AND (isnull(@TypeVerrouMaitre,0)=0) AND
(isnull(@OperateurMaitre,0)=0) --@@ROWCOUNT=1
BEGIN
insert debug (msg) values('RAISERROR('' ROLLBACK Pas de verrou'' , 16, 1)
ROLLBACK TRANSACTION')
END
ELSE
BEGIN
IF (@StationMaitre=@Station) AND (@TypeVerrouMaitre=@TypeVerrou) AND
(@OperateurMaitre=@Operateur)
BEGIN
insert debug (msg) values ('OK')
END
ELSE
BEGIN
insert debug (msg) values ('RAISERROR('' ROLLBACK Mauvais operateur'', 16,
1 )
ROLLBACK TRANSACTION')
END
END

GO

La table debug me permet juste de "voir" le résultat théorique du trigger.
En prod, j'enlèverai l'insert debug pour ne laisser que le contenu de la
valeur ajoutée.

Le problème est que pour modifier 1 seul champ dans la table, j'ai
l'impression que le trigger est activé 3 fois : 2 fois en passant par PAS DE
VERROU et la dernière fois par OK
En production cela ne fonctionnera pas car le Rollback annulera la
transaction avant d'être arrivé à terme.

Je n'arrive pas à comprendre pourquoi il y a des activations qui ne
correspondent pas à ce que je souhaiterai !


Merci de votre aide.
ac at expedito dot fr


Avatar
Sylvain Lafontaine
À mon avis, vouloir utiliser les triggers pour valider une
insertion/modification APRÈS-COUP n'est probablement pas une bonne idée.

Vous seriez mieux de faire cette vérification avant même d'essayer de faire
la mise-à-jour, dans une procédure stockée ou même dans votre code VBA;
sinon vous allez finir par avoir du code complètement immangeable. Comme
les trois champs doivent être identiques entre deux tables différentes, on
peut également se poser des questions sur la Normalité de votre bdd.

Les triggers ne devraient être utilisées que pour des choses simples, comme
la mise-à-jour d'un champ stockant la dernière date de modification et autre
gugus du genre; sinon votre bdd va devenir un vrai labyrinthe.

S. L.

"Arnaud CAVELIER" wrote in
message news:
Bonjour,

J'espère que vous allez pouvoir m'aider car je commence à vraiment tourner
en rond :

Dans mon Trigger, je souhaite comparer 3 valeurs de la table INSERTED à 3
trois valeurs lues dans une autre table pour valider la modification de la
ligne :

Si 1 des 3 valeurs n'est pas identique, je dois annuler la mise à jour
(Rollback).

Voici un peu de code pour être plus clair :
ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
FOR UPDATE
AS
DECLARE @StationMaitre nvarchar(50)
DECLARE @TypeVerrouMaitre tinyint
DECLARE @OperateurMaitre int
DECLARE @Station nvarchar(50)
DECLARE @TypeVerrou tinyint
DECLARE @Operateur int
DECLARE @CptExpedition int
DECLARE @Message nvarchar(4000)
SELECT
@CptExpedition=CptExpedition,@Station=Station,@TypeVerrou=TypeVerrou,@Operateur=Operateur
FROM inserted
SELECT
@StationMaitre=Station,@TypeVerrouMaitre=TypeVerrou,@OperateurMaitre=Operateur
FROM VERROU WHERE RefCpt=@CptExpedition
Select @Message=' MAJ Exped '+ convert(nvarchar,@CptExpedition) + ' St=' +
convert(nvarchar,@Station) + '=' + convert(nvarchar,@StationMaitre) +'
Op='+convert(nvarchar,@Operateur)+'='+convert(nvarchar,@OperateurMaitre)+'
TypVer=' + convert(nvarchar,@TypeVerrou) + '=' +
convert(nvarchar,@TypeVerrouMaitre)
insert debug (msg) values (@Message)
IF (isnull(len(@StationMaitre),0)=0) AND (isnull(@TypeVerrouMaitre,0)=0)
AND
(isnull(@OperateurMaitre,0)=0) --@@ROWCOUNT=1
BEGIN
insert debug (msg) values('RAISERROR('' ROLLBACK Pas de verrou'' , 16, 1)
ROLLBACK TRANSACTION')
END
ELSE
BEGIN
IF (@StationMaitre=@Station) AND (@TypeVerrouMaitre=@TypeVerrou) AND
(@OperateurMaitre=@Operateur)
BEGIN
insert debug (msg) values ('OK')
END
ELSE
BEGIN
insert debug (msg) values ('RAISERROR('' ROLLBACK Mauvais operateur'', 16,
1 )
ROLLBACK TRANSACTION')
END
END

GO

La table debug me permet juste de "voir" le résultat théorique du trigger.
En prod, j'enlèverai l'insert debug pour ne laisser que le contenu de la
valeur ajoutée.

Le problème est que pour modifier 1 seul champ dans la table, j'ai
l'impression que le trigger est activé 3 fois : 2 fois en passant par PAS
DE
VERROU et la dernière fois par OK
En production cela ne fonctionnera pas car le Rollback annulera la
transaction avant d'être arrivé à terme.

Je n'arrive pas à comprendre pourquoi il y a des activations qui ne
correspondent pas à ce que je souhaiterai !


Merci de votre aide.
ac at expedito dot fr


Avatar
Arnaud CAVELIER
Ce sont effectivement mes débuts avec des triggers.

MERCI des infos

A bientôt,
Arnaud

"Fred BROUARD" a écrit :

Un trigger ne fonctionne pas comme tu le souhaite ! Il fonctionne comme il doit
fonctionner...

Un trigger voit passer d'un seul coup TOUTES les modifs de TOUS les tuples en jeu.

Ainsi, lors de l'exécution de l'ordre :

UPDATE EXPEDITION
SET Operateur = UPPER(Operateur)

Toutes les lignes de la table seront modifiées.
Ce qui veur dire que la pseudo table inserted contiendra autant de lignes qu'il
y a de lignes dans la table EXPEDITION.

Donc ton code est déjà anormal puisqu'il présuppose que inserted ne contient
jamais qu'une seule ligne !

en effet, a quelle ligne se réfère :
SELECT @CptExpedition=CptExpedition,
@Station=Station,
@TypeVerrou=TypeVerrou,
@Operateur=Operateur
FROM inserted
???

Pour gérer les effets d'un triggers, il faut, soit gérer le contrôle de manière
ensembliste (requête avec intégration de la table inserted liée aux tables en
jeu), soit utiliser un curseur (beaucoup moins performant, mais nécessaire
lorque le traitement est "au cas par cas").

En l'occurence et à mon avis, ton trigger peut se résumer à :

ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
FOR UPDATE
AS

IF EXISTS (SELECT *
FROM inserted i
INNER JOIN VERROU v
ON i.CptExpedition = v.RefCpt
WHERE i.Station <> v.Station
OR i.TypeVerrou <> v.TypeVerrou
OR i.Operateur <> v.Operateur)
ROLLBACK TRANSACTION
GO

C'est tout !

En régle générale, la présence de variable dans un trigger est un bon indice
pour montrer que quelque chose ne va pas !

A +

--
Frédéric BROUARD, MVP SQL Server. Expert SQL / spécialiste Delphi, web
Livre SQL - col. Référence : http://sqlpro.developpez.com/bookSQL.html
Le site du SQL, pour débutants et pros : http://sqlpro.developpez.com
************************ www.datasapiens.com *************************



Arnaud CAVELIER a écrit:
> Bonjour,
>
> J'espère que vous allez pouvoir m'aider car je commence à vraiment tourner
> en rond :
>
> Dans mon Trigger, je souhaite comparer 3 valeurs de la table INSERTED à 3
> trois valeurs lues dans une autre table pour valider la modification de la
> ligne :
>
> Si 1 des 3 valeurs n'est pas identique, je dois annuler la mise à jour
> (Rollback).
>
> Voici un peu de code pour être plus clair :
> ALTER TRIGGER tr_Verrou_Expedition ON [dbo].[EXPEDITION]
> FOR UPDATE
> AS
> DECLARE @StationMaitre nvarchar(50)
> DECLARE @TypeVerrouMaitre tinyint
> DECLARE @OperateurMaitre int
> DECLARE @Station nvarchar(50)
> DECLARE @TypeVerrou tinyint
> DECLARE @Operateur int
> DECLARE @CptExpedition int
> DECLARE @Message nvarchar(4000)
> SELECT
> @CptExpedition=CptExpedition,@Station=Station,@TypeVerrou=TypeVerrou,@Operateur=Operateur FROM inserted
> SELECT
> @StationMaitre=Station,@TypeVerrouMaitre=TypeVerrou,@OperateurMaitre=Operateur FROM VERROU WHERE RefCpt=@CptExpedition
> Select @Message=' MAJ Exped '+ convert(nvarchar,@CptExpedition) + ' St=' +
> convert(nvarchar,@Station) + '=' + convert(nvarchar,@StationMaitre) +'
> Op='+convert(nvarchar,@Operateur)+'='+convert(nvarchar,@OperateurMaitre)+'
> TypVer=' + convert(nvarchar,@TypeVerrou) + '=' +
> convert(nvarchar,@TypeVerrouMaitre)
> insert debug (msg) values (@Message)
> IF (isnull(len(@StationMaitre),0)=0) AND (isnull(@TypeVerrouMaitre,0)=0) AND
> (isnull(@OperateurMaitre,0)=0) --@@ROWCOUNT=1
> BEGIN
> insert debug (msg) values('RAISERROR('' ROLLBACK Pas de verrou'' , 16, 1)
> ROLLBACK TRANSACTION')
> END
> ELSE
> BEGIN
> IF (@StationMaitre=@Station) AND (@TypeVerrouMaitre=@TypeVerrou) AND
> (@OperateurMaitre=@Operateur)
> BEGIN
> insert debug (msg) values ('OK')
> END
> ELSE
> BEGIN
> insert debug (msg) values ('RAISERROR('' ROLLBACK Mauvais operateur'', 16,
> 1 )
> ROLLBACK TRANSACTION')
> END
> END
>
> GO
>
> La table debug me permet juste de "voir" le résultat théorique du trigger.
> En prod, j'enlèverai l'insert debug pour ne laisser que le contenu de la
> valeur ajoutée.
>
> Le problème est que pour modifier 1 seul champ dans la table, j'ai
> l'impression que le trigger est activé 3 fois : 2 fois en passant par PAS DE
> VERROU et la dernière fois par OK
> En production cela ne fonctionnera pas car le Rollback annulera la
> transaction avant d'être arrivé à terme.
>
> Je n'arrive pas à comprendre pourquoi il y a des activations qui ne
> correspondent pas à ce que je souhaiterai !
>
>
> Merci de votre aide.
> ac at expedito dot fr