Hello à tous,

J'ai une procédure qui fait un merge (d'une table sur base d'un tvp passé en paramètre) suivi d'un delete. Le souci est que j'ai également un index unique filtré sur cette table et du coup, dans certains cas, le merge génère une erreur vis-à-vis de cette index unique alors qu'en fait, la situation serait corrigée par le delete.

Vu que j'ai besoin de cet index pour assuré la cohérence des données au cas où un p'tit malin irait joué avec des requêtes ad-hoc dans la db, je cherche un moyen de concilier ces deux instructions (merge et delete) en une seule (j'imagine un merge).

J'ai bien vu qu'on pouvait mettre une clause delete dans le merge mais je n'ai pas trouvé comment faire sans supprimer de la table tout ce qui n'est pas dans le tvp...

Voici donc le code de la procédure (que j'ai édulcoré des passages qui ne concernent pas cette table) (j'ai remis le code complet car je me suis rendu compte qu'une solution possible avec la version édulcorée aurait été de mettre le delete avant le merge mais alors, il pourrait y avoir une violation de clef étrangère pour la table S_PROMO.T_PRODUCT_LIST_DEMO_PLD (à moins de mettre on delete cascade mais bon...)) :
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
80
ALTER PROCEDURE [S_PROMO].[UP_PROMO_SAVE_DEMO]
    @PRM_ID                INT,
    @DETAILS_DEMO        DETAILS_DEMO READONLY,
    @BA_ID                TINYINT,
    @PRODUCTS            PRODUCT_LIST READONLY,
    @PCT_ID                TINYINT
AS
BEGIN
    SET NOCOUNT ON;
 
    SET XACT_ABORT ON;
 
    BEGIN TRAN
 
    DECLARE @T_DTD AS TABLE(DTD_ID INT NOT NULL);
 
    MERGE S_PROMO.T_DETAIL_DEMO_DTD AS TARGET
    USING (    SELECT  CONT_ID, DTD_PERCENT, DTD_DESC, DTD_NOTE
            FROM    @DETAILS_DEMO ) AS SOURCE(CONT_ID, DTD_PERCENT, DTD_DESC, DTD_NOTE)
        ON    TARGET.PRM_ID = @PRM_ID
        AND    TARGET.CONT_ID = SOURCE.CONT_ID
        AND TARGET.DTD_PERCENT = SOURCE.DTD_PERCENT
    WHEN MATCHED THEN
        UPDATE    SET    DTD_PERCENT = SOURCE.DTD_PERCENT,
                    DTD_DESC    = SOURCE.DTD_DESC,
                    DTD_NOTE    = SOURCE.DTD_NOTE,
                    PCT_ID        = @PCT_ID
    WHEN NOT MATCHED THEN    
        INSERT(PRM_ID, CONT_ID, DTD_PERCENT, DTD_DESC, DTD_NOTE, BA_ID, PCT_ID)
        VALUES(@PRM_ID, SOURCE.CONT_ID, SOURCE.DTD_PERCENT, SOURCE.DTD_DESC, SOURCE.DTD_NOTE, @BA_ID, @PCT_ID)
        OUTPUT INSERTED.DTD_ID INTO @T_DTD;
 
    MERGE S_PROMO.T_PRODUCT_LIST_DEMO_PLD AS TARGET
    USING (    SELECT    DTD.DTD_ID,
                    P.PLD_BARCODE,
                    P.PLD_INCLUSIVE,
                    P.PLD_COLOR,
                    P.PLD_SIZE,
                    P.PLD_DESC,
                    P.PLD_PRICE
            FROM    @PRODUCTS P
                        INNER JOIN S_PROMO.T_DETAIL_DEMO_DTD DTD
                            ON    DTD.PRM_ID = @PRM_ID
                            AND    DTD.CONT_ID = P.CONT_ID
                            AND DTD.DTD_PERCENT = P.DTD_PERCENT) AS SOURCE(DTD_ID, PLD_BARCODE, PLD_INCLUSIVE, PLD_COLOR, PLD_SIZE, PLD_DESC, PLD_PRICE)
        ON    TARGET.DTD_ID = SOURCE.DTD_ID
        AND TARGET.PLD_BARCODE = SOURCE.PLD_BARCODE
    WHEN MATCHED THEN
        UPDATE    SET    PLD_INCLUSIVE = SOURCE.PLD_INCLUSIVE,
                    PLD_COLOR = SOURCE.PLD_COLOR,
                    PLD_SIZE = SOURCE.PLD_SIZE,
                    PLD_DESC = SOURCE.PLD_DESC,
                    PLD_PRICE = SOURCE.PLD_PRICE
    WHEN NOT MATCHED THEN
        INSERT(DTD_ID, PLD_BARCODE, PLD_INCLUSIVE, PLD_COLOR, PLD_SIZE, PLD_DESC, PLD_PRICE)
        VALUES(SOURCE.DTD_ID, SOURCE.PLD_BARCODE, SOURCE.PLD_INCLUSIVE, SOURCE.PLD_COLOR, SOURCE.PLD_SIZE, SOURCE.PLD_DESC, SOURCE.PLD_PRICE);
 
    DELETE FROM S_PROMO.T_PRODUCT_LIST_DEMO_PLD
    FROM    S_PROMO.T_PRODUCT_LIST_DEMO_PLD PLD
                INNER JOIN S_PROMO.T_DETAIL_DEMO_DTD DTD
                    ON    PLD.DTD_ID = DTD.DTD_ID
                    AND DTD.PRM_ID = @PRM_ID
    WHERE    PLD.DTD_ID IN (SELECT DTD_ID FROM @T_DTD)
        AND NOT EXISTS (SELECT    1
                        FROM    @PRODUCTS P
                        WHERE    P.CONT_ID = DTD.CONT_ID
                            AND    P.DTD_PERCENT = DTD.DTD_PERCENT
                            AND P.PLD_BARCODE = PLD.PLD_BARCODE);
 
    DELETE FROM S_PROMO.T_DETAIL_DEMO_DTD
    FROM    S_PROMO.T_DETAIL_DEMO_DTD DTD
                INNER JOIN @DETAILS_DEMO DTL
                    ON    DTD.CONT_ID = DTL.CONT_ID
                    AND DTD.PRM_ID = @PRM_ID
    WHERE    NOT EXISTS (SELECT    1
                        FROM    @DETAILS_DEMO D
                        WHERE    D.CONT_ID = DTD.CONT_ID
                            AND D.DTD_PERCENT = DTD.DTD_PERCENT);
 
    COMMIT TRAN;
Le ddl de la table en question :
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
CREATE TABLE [S_PROMO].[T_DETAIL_DEMO_DTD](
    [DTD_ID] [int] IDENTITY(-2147483648,1) NOT NULL,
    [PRM_ID] [int] NOT NULL,
    [CONT_ID] [int] NOT NULL,
    [DTD_PERCENT] [decimal](4, 2) NOT NULL,
    [DTD_DESC] [varchar](200) NULL,
    [DTD_NOTE] [varchar](200) NULL,
    [BA_ID] [tinyint] NOT NULL,
    [DTD_ENCODED] [bit] NOT NULL,
    [PCT_ID] [tinyint] NULL,
    [DTD_CREATED_ON] [datetime] NOT NULL,
    [DTD_CREATED_BY] [varchar](100) NOT NULL,
    [DTD_MODIFIED_ON] [datetime] NULL,
    [DTD_MODIFIED_BY] [varchar](100) NULL,
 CONSTRAINT [PK_T_DETAIL_DEMO_DTD] PRIMARY KEY CLUSTERED 
(
    [DTD_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
 CONSTRAINT [AK_T_DETAIL_DEMO_DTD] UNIQUE NONCLUSTERED 
(
    [PRM_ID] ASC,
    [CONT_ID] ASC,
    [DTD_PERCENT] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
 
GO
 
SET ANSI_PADDING OFF
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] ADD  CONSTRAINT [DF_T_DETAIL_DEMO_DTD_DTD_ENCODED]  DEFAULT ((0)) FOR [DTD_ENCODED]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] ADD  CONSTRAINT [DF_T_DETAIL_DEMO_DTD_PCT_ID]  DEFAULT ((0)) FOR [PCT_ID]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] ADD  CONSTRAINT [DF__T_DETAIL___DTD_C__40E5634A]  DEFAULT (getdate()) FOR [DTD_CREATED_ON]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] ADD  CONSTRAINT [DF__T_DETAIL___DTD_C__41D98783]  DEFAULT (suser_sname()) FOR [DTD_CREATED_BY]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD]  WITH CHECK ADD  CONSTRAINT [FK_DTD_TO_BA] FOREIGN KEY([BA_ID])
REFERENCES [dbo].[T_BUREAU_ACHAT_BA] ([BA_ID])
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] CHECK CONSTRAINT [FK_DTD_TO_BA]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD]  WITH CHECK ADD  CONSTRAINT [FK_DTD_TO_CONT_ID] FOREIGN KEY([CONT_ID])
REFERENCES [S_CONTRAT].[T_CONTRAT_DEMO_CONT] ([CONT_ID])
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] CHECK CONSTRAINT [FK_DTD_TO_CONT_ID]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD]  WITH CHECK ADD  CONSTRAINT [FK_DTD_TO_PCT] FOREIGN KEY([PCT_ID])
REFERENCES [S_PROMO].[T_PERCENT_TYPE_PCT] ([PCT_ID])
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] CHECK CONSTRAINT [FK_DTD_TO_PCT]
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD]  WITH CHECK ADD  CONSTRAINT [FK_DTD_TO_PRM] FOREIGN KEY([PRM_ID])
REFERENCES [S_PROMO].[T_PROMO_PRM] ([PRM_ID])
GO
 
ALTER TABLE [S_PROMO].[T_DETAIL_DEMO_DTD] CHECK CONSTRAINT [FK_DTD_TO_PRM]
GO
Le ddl de l'index unique filtré :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
CREATE UNIQUE NONCLUSTERED INDEX [UQ_PRM_ID_CONT_ID] ON [S_PROMO].[T_DETAIL_DEMO_DTD]
(
    [PRM_ID] ASC,
    [CONT_ID] ASC
)
WHERE ([PCT_ID]=(0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Et pour être sûr d'être complet, le ddl du tvp :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
CREATE TYPE [S_PROMO].[DETAILS_DEMO] AS TABLE(
    [CONT_ID] [int] NOT NULL,
    [DTD_PERCENT] [decimal](4, 2) NOT NULL,
    [DTD_DESC] [varchar](50) NULL,
    [DTD_NOTE] [varchar](50) NULL,
    PRIMARY KEY CLUSTERED 
(
    [CONT_ID] ASC,
    [DTD_PERCENT] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO