Bonjour,
J'ai un problème qui m'empêche de dormir (du coup je suis pas certain d'avoir les idées très claires...)
J'ai mettons une entreprise qui étudie des dossiers (appels d'offre par exemple).
Pour chaque dossier, elle va émettre une ou plusieurs offres.
Ces offres comportent des "potentiels", à savoir des lignes de produit avec un chiffre d'affaire envisagé.
Le potentiel de toutes les offres d'un dossier en cours est calculé en prenant le max(ca) pour chaque produit parmi toutes les offres d'un dossier.
Cependant, au bout d'un moment, une offre est retenue parmi toutes, et à ce moment le potentiel devient la liste des CA de l'offre gagnante.
Au départ, je suis parti sur cette structure :
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 create table dossier ( id int primary key identity, nom varchar(10) unique, id_gagnante int null references offre(id) ); create table offre ( id int primary key identity, id_dossier int not null references dossier(id), nom varchar(10) unique, unique (id_dossier, id) ); go create table potentiel ( id int primary key identity, id_offre int not null references offre(id), produit varchar(10) not null, ca int not null );
Outre la clé étrangère sur dossier.id_gagnante qui n'est pas très pratique à créer, cette solution, pourtant la première qui m'est venue à l'esprit, ne me plaît pas trop dans la mesure où je vais avoir de nombreux NULL dans cette colonne, ce qui ne va pas arranger les performances de ma requête de calcul des potentiels (et ça fait un moment que je lis sur le forum que les NULL c'est pour les nuls).
Et au final, telle qu'elle, cette solution ne garanti pas que l'offre gagnante fait partie du dossier.
Je me suis donc dit "ok, ben c'est facile, il suffit de faire une table "gagnante" qui hérite de dossier, et contient la référence à l'offre du dossier qui est gagnante.
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 create table gagnante ( id_dossier int primary key references dossier(id), id_offre int references offre(id) );
Sauf que là, je ne garanti pas que l'offre gagnante est bien rattachée à son dossier.
Du coup, je dois rajouter une clé étrangère de plus :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 create table gagnante ( id_dossier int primary key references dossier(id), id_offre int references offre(id), foreign key (id_dossier, id_offre) references offre(id_dossier, id) );
Sauf que offre(id_dossier, id) n'est pas une clé candidate dans offre. Le fait de la créer la rend complètement redondante avec offre(id) dans la mesure où cette dernière est déjà la clé primaire : ça ne me plait pas.
J'ai donc pensé à faire autrement : dériver de offre, et rendre unique le numéro de dossier :
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 create table gagnante ( id_offre int primary hey references offre(id), id_dossier int not null unique );
Seulement à nouveau, aucun moyen de garantir que id_dossier est bien celui qui vient de l'offre.
J'ai tenté une computed column, mais le fait d'aller chercher l'id du dossier de l'offre dans une autre table n'est pas déterministe, et donc impossible de mettre un index unique dessus.
Je peux toujours m'en sortir en faire une clé étrangère sur offre(id, id_dossier) mais c'est toujours aussi moche.
Reste alors la solution du trigger : aller vérifier, pour chaque modification, que le dossier est bien celui de l'offre gagnante.
Seulement, pas moyen de faire une jointure entre inserted et deleted à coup sûr sans ajouter une clé primaire auto-incrémentée. Ce qui ne me plaît pas du tout non plus vu que ça perd de la place pour rien, et fait sauter mon héritage : du coup je suis obligé de supprimer toutes les lignes de deleted puis d'insérer toutes les lignes de inserted... c'est presque encore plus moche...
Toute suggestion est la bienvenue
Ce qui serait vraiment top, c'est un mécanisme permettant de créer un index unique sur gagnante(offre.id_dossier), offre.id_dossier étant déductible de gagnante.id_offre sans même avoir à recopier l'info dans la table gagnante...
En gros, une linked column plutôt qu'une computed column. J'imagine que ça existe pas...
Pourtant ça résoudrait mon problème de manière élégante et éviterait de coller un trigger tout moche pour calculer le dossier en fonction de l'offre
Voici ce que j'ai pour le moment :
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 drop view v_potentiel; drop table potentiel; drop table gagnante; drop table offre; drop table dossier; go create table dossier ( id int primary key identity, nom varchar(10) unique ); create table offre ( id int primary key identity, id_dossier int not null references dossier(id), nom varchar(10) unique, unique (id_dossier, id) ); go create table gagnante ( id_dossier int primary key references dossier(id), id_offre int references offre(id), foreign key (id_dossier, id_offre) references offre(id_dossier, id) ); go create table potentiel ( id int primary key identity, id_offre int not null references offre(id), produit varchar(10) not null, ca int not null ); go create trigger trg_gagnante_dossier on gagnante instead of insert, update as begin delete gagnante where id_offre in (select id_offre from deleted); insert into gagnante (id_offre, id_dossier) select i.id_offre, o.id_dossier from inserted i inner join offre o on o.id = i.id_offre; end; go create view v_potentiel (dossier, produit, ca) as select d.nom, p.produit, max(p.ca) from dossier d inner join offre o on o.id_dossier = d.id inner join potentiel p on p.id_offre = o.id where not exists ( select null from gagnante g where g.id_dossier = d.id ) group by d.nom, p.produit union all select d.nom, p.produit, p.ca from gagnante g inner join dossier d on d.id = g.id_dossier inner join offre o on o.id = g.id_offre inner join potentiel p on p.id_offre = o.id go insert into dossier (nom) values ('Dossier 1'), ('Dossier 2'); insert into offre (id_dossier, nom) values (1, 'Offre 1.1'), (1, 'Offre 1.2'), (2, 'Offre 2.1'); insert into potentiel (id_offre, produit, ca) values (1, 'P1', 500), (1, 'P2', 100), (1, 'P3', 200), (2, 'P1', 1000), (2, 'P3', 50), (3, 'P1', 800), (3, 'P2', 1500); select dossier, produit, ca from v_potentiel order by dossier, produit, ca; insert into gagnante (id_offre) values (2); select dossier, produit, ca from v_potentiel order by dossier, produit, ca; update gagnante set id_offre = 1 where id_dossier = 1; select dossier, produit, ca from v_potentiel order by dossier, produit, ca; select * from gagnante update gagnante set id_offre = 3 where id_dossier = 1; select * from gagnante update gagnante set id_dossier = 1 where id_offre = 3; select * from gagnante
Partager