Bonjour,

J'utilise Microsoft SQL Server 2014 (SP2-CU9) (KB4055557) - 12.0.5563.0 (X64) Dec 7 2017 01:00:06 Copyright (c) Microsoft Corporation Standard Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)

J'ai des traitements effectués par des triggers, je et me trouve dans une situation où je souhaite que selon l'origine des modifications de certaines données, certains triggers se déclenchent ou non.

Pour faire "simple" (*sic*) j'ai trois tables A, B et C, dont j'historise certaines modifications dans une quatrième table LOG.

[code=sql]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 
/*
Pour rejouter le script
 
drop table LOG;
drop table C;
drop table B;
drop table A;
go
*/
 
/*
Statuts :
	1 - Créé
	2 - Gagné
	3 - Perdu
*/
 
create table A
(
     id int identity primary key,
     name char(10),
     statut tinyint
);
 
create table B
(
     id int identity primary key,
     name char(12),
     statut tinyint,
     a_id int references A(id)
);
 
create table C
(
     id int identity primary key,
     name char(14),
     statut tinyint,
     b_id int references B(id)
);
 
create table LOG
(
     id int identity primary key,
     a_id int not null references A(id),
	 a_old_statut tinyint not null,
	 a_new_statut tinyint not null,
     b_id int null references B(id), 
	 b_old_statut tinyint null,
	 b_new_statut tinyint null,
     c_id int null references C(id),
	 c_old_statut tinyint null,
	 c_new_statut tinyint null,
	 timestamp datetime2 not null default CURRENT_TIMESTAMP
);
go
 
set IDENTITY_INSERT A ON
insert into A (id, name, statut) values (1, 'Affaire 1', 1), (2, 'Affaire 2', 1), (3, 'Affaire 3', 1);
set IDENTITY_INSERT A OFF
 
set IDENTITY_INSERT B ON
insert into B (id, name, statut, a_id) values (1, 'Dossier A1D1', 1, 1), (2, 'Dossier A1D2', 1, 1), (3, 'Dossier A1D3', 1, 1),
                                              (4, 'Dossier A2D1', 1, 2), (5, 'Dossier A2D2', 1, 2), (6, 'Dossier A2D3', 1, 2),
                                              (7, 'Dossier A3D1', 1, 3), (8, 'Dossier A3D2', 1, 3), (9, 'Dossier A3D3', 1, 3)
set IDENTITY_INSERT B OFF
 
set IDENTITY_INSERT C ON
insert into C (id, name, statut, b_id) values (1, 'Etude A1D1E1', 1, 1), (2, 'Etude A1D1E2', 1, 1), (3, 'Etude A1D1E3', 1, 1), 
											  (4, 'Etude A1D2E1', 1, 1), (5, 'Etude A1D2E2', 1, 1), (6, 'Etude A1D2E3', 1, 1), 
											  (7, 'Etude A1D3E1', 1, 1), (8, 'Etude A1D3E2', 1, 1), (9, 'Etude A1D3E3', 1, 1),
                                              (10, 'Etude A2D1E1', 1, 2), (11, 'Etude A2D1E2', 1, 1), (12, 'Etude A2D1E3', 1, 1), 
											  (13, 'Etude A2D2E1', 1, 2), (14, 'Etude A2D2E2', 1, 1), (15, 'Etude A2D2E3', 1, 1), 
											  (16, 'Etude A2D3E1', 1, 2), (17, 'Etude A2D3E2', 1, 1), (18, 'Etude A2D3E3', 1, 1),
                                              (19, 'Etude A3D1E1', 1, 3), (20, 'Etude A3D1E2', 1, 1), (21, 'Etude A3D1E3', 1, 1), 
											  (22, 'Etude A3D2E1', 1, 3), (23, 'Etude A3D2E2', 1, 1), (24, 'Etude A3D2E3', 1, 1), 
											  (25, 'Etude A3D3E1', 1, 3), (26, 'Etude A3D3E2', 1, 1), (27, 'Etude A3D3E3', 1, 1)
set IDENTITY_INSERT C OFF
go
J'ai des règles de gestion (en réalité bien plus complexes) qui permettent de mettre à jour les statuts de A et B en fonction de C, en cascade.

Jusqu'à présent les seules modifications possibles de statut se faisaient dans C.

J'avais donc écrit un trigger de ce genre :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 
create trigger TRG_C on C after update
as
begin
	if (rowcount_big() > 0 and update(statut))
	begin
		create table #tmp_log (
			 a_id int not null,
			 a_old_statut tinyint not null,
			 a_new_statut tinyint null,
			 b_id int null, 
			 b_old_statut tinyint null,
			 b_new_statut tinyint null,
			 c_id int null,
			 c_old_statut tinyint null,
			 c_new_statut tinyint null
		);
 
		-- Historisation des statuts qu'ils étaient avant déclenchement du trigger
		insert into #tmp_log
		select a.id, a.statut, null, b.id, b.statut, null, i.id, d.statut, i.statut
		from inserted i
		inner join deleted d on d.id = i.id and d.statut <> i.statut
		inner join b on b.id = i.b_id
		inner join a on a.id = b.a_id;
 
		-- Mise à jour de B à partir du changement de statut de C
		update b
		set statut = z.statut
		from inserted i
		inner join b on b.id = i.b_id
		inner join (select b_id, max(statut) statut from c group by b_id) z on z.b_id = b.id;
 
		-- Mise à jour de A à partir du changement de statut de B en cascade à partir de C
		update a
		set statut = z.statut
		from inserted i 
		inner join b on b.id = i.b_id
		inner join a on a.id = b.a_id
		inner join (select a_id, max(statut) statut from b group by a_id) z on z.a_id = a.id;
 
		-- Mise à jour des nouveaux statuts de A et B dans la table temporaire
		update l
		set a_new_statut = a.statut, b_new_statut = b.statut
		from #tmp_log l
		inner join b on b.id = l.b_id
		inner join a on a.id = l.a_id;
 
		insert into log (a_id, a_old_statut, a_new_statut, b_id, b_old_statut, b_new_statut, c_id, c_old_statut, c_new_statut)
		select a_id, a_old_statut, a_new_statut, b_id, b_old_statut, b_new_statut, c_id, c_old_statut, c_new_statut
		from #tmp_log;
 
		drop table #tmp_log;
	end;
end;
go

Ce qui permettait d'avoir ça comme comportement :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
 
update C set statut = 2 where id = 1;
update C set statut = 3 where id = 2;
update C set statut = 2 where b_id = 2;
update C set statut = 3 where id = 13;
update C set statut = 1 where statut > 1;
 
select * from log;

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(3*lignes affectées)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(3*lignes affectées)
 
(3*lignes affectées)
 
(3*lignes affectées)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(1 ligne affectée)
 
(5*lignes affectées)
 
(2*lignes affectées)
 
(1 ligne affectée)
 
(5*lignes affectées)
 
(5*lignes affectées)
 
(5*lignes affectées)
id          a_id        a_old_statut a_new_statut b_id        b_old_statut b_new_statut c_id        c_old_statut c_new_statut timestamp
----------- ----------- ------------ ------------ ----------- ------------ ------------ ----------- ------------ ------------ ---------------------------
1           1           1            2            1           1            2            1           1            2            2021-02-18 15:25:27.3200000
2           1           2            3            1           2            3            2           1            3            2021-02-18 15:25:27.3333333
3           1           3            3            2           1            2            16          1            2            2021-02-18 15:25:27.3566667
4           1           3            3            2           1            2            13          1            2            2021-02-18 15:25:27.3566667
5           1           3            3            2           1            2            10          1            2            2021-02-18 15:25:27.3566667
6           1           3            3            2           2            3            13          2            3            2021-02-18 15:25:27.3700000
7           1           3            1            2           3            1            16          2            1            2021-02-18 15:25:27.3900000
8           1           3            1            2           3            1            13          3            1            2021-02-18 15:25:27.3900000
9           1           3            1            2           3            1            10          2            1            2021-02-18 15:25:27.3900000
10          1           3            1            1           3            1            2           3            1            2021-02-18 15:25:27.3900000
11          1           3            1            1           3            1            1           2            1            2021-02-18 15:25:27.3900000
 
(11*lignes affectées)
 
 
Heure de fin*: 2021-02-18T15:25:27.4687477+01:00
Sauf que là, finalement, on doit pouvoir aussi mettre à jour directement B, sans passer par C.

Je dois donc écrire un trigger qui va :
- Passer toutes les lignes de C au même statut que B lorsque B change de statut
- Recalculer le statut de A en fonction du nouveau statut de B

Cependant, pour cette action :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
 
update B set statut = 2 where id = 2;

Je ne souhaite pas avoir la liste des changements de statut de C dans LOG, mais uniquement une ligne dans LOG avec le changement de statut de B, et les statuts de B dans c_old_statut et c_new_statut et NULL dans c_id.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
 
id          a_id        a_old_statut a_new_statut b_id        b_old_statut b_new_statut c_id        c_old_statut c_new_statut timestamp
----------- ----------- ------------ ------------ ----------- ------------ ------------ ----------- ------------ ------------ ---------------------------
12          1           1            2            2           1            2            NULL        1            2            2021-02-18 15:28:27.3333333
Seulement, si je crée un trigger dans B qui fait ça, il va déclencher des modifications dans C, qui vont loguer aussi les modifications de C, que je ne veux pas.
Inversement, le trigger actuel sur C met à jour B, mais je ne veux pas que dans ce cas je génère une ligne depuis le trigger sur B !

Comment faire (est-ce possible) au sein de mes triggers de dire "ne déclenche pas les triggers" (si possible avec explicitement le nom des triggers en question) pendant mes mises à jour ?

Je pourrais jouer avec des variables globales, vérifier l'existence de #tmp, etc. mais est-ce qu'il y a plus propre/fiable ?

Genre :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
 
                disable trigger TRG_B on B;
		update b
		set statut = z.statut
		from inserted i
		inner join b on b.id = i.b_id
		inner join (select b_id, max(statut) statut from c group by b_id) z on z.b_id = b.id;
                enable trigger TRG_B on B;

Mais uniquement dans le périmètre de ma transaction : si quelqu'un modifie B en parallèle, je ne veux surtout pas empêcher le trigger de se déclencher !