Je veux créer un déclencheur pour vérifier si un enregistrement existe avant l'insertion, s'il existe une restauration, sinon continuer à faire l'insertion. Le truc, c'est que lorsque je fais l'insertion, il revient toujours en arrière. que devrais-je faire?

ALTER TRIGGER [dbo].[CHECKCONSOMMATION]
ON [dbo].[ConsommationEau]
FOR INSERT
AS
DECLARE @IDABONNEMENT INT
DECLARE @DEFMONTH DATETIME

SELECT @IDABONNEMENT = inserted.idAbonnement FROM inserted
SELECT @DEFMONTH = inserted.Periode FROM inserted


IF EXISTS (SELECT 1 FROM ConsommationEau WHERE idAbonnement = @IDABONNEMENT AND DATEDIFF(MONTH, Periode, @DEFMONTH) = 0)
    BEGIN
    RAISERROR('THIS RECORD IS ALREADY EXISTS', 10, 1)
    ROLLBACK
    RETURN
    END

Et ceci ma table.

     USE [GESTEAU]
      GO

 /****** Object:  Table [dbo].[ConsommationEau]    Script Date: 4/20/2017  
 :08:53 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

 CREATE TABLE [dbo].[ConsommationEau](
[idConsomationEau] [int] IDENTITY(1,1) NOT NULL,
[Periode] [date] NULL,
[Qte] [int] NULL,
[idAbonnement] [int] NULL
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[ConsommationEau]  WITH CHECK ADD FOREIGN 
KEY([idAbonnement])
REFERENCES [dbo].[AbonnementEau] ([idAbonnement])
 GO
0
카림 사부 20 avril 2017 à 16:16

3 réponses

Meilleure réponse

Si l'intention est que la table ne puisse contenir qu'une seule ligne, par idAbonnement, par mois, alors je n'utiliserais pas de déclencheur. J'utiliserais une colonne calculée persistante et un index unique:

CREATE TABLE [dbo].[ConsommationEau](
[idConsomationEau] [int] IDENTITY(1,1) NOT NULL,
[Periode] [date] NULL,
[Qte] [int] NULL,
[idAbonnement] [int] NULL,
PeriodeMonth as DATEADD(month,DATEDIFF(month,0,Periode),0) persisted
) ON [PRIMARY]
GO
create unique index IX_ConsommationEau_Monthly
    on ConsommationEau (idAbonnement, PeriodeMonth)

De cette façon, vous avez déclaré ce qui devrait être unique plutôt que d'avoir à écrire du code procédural , et vous avez évité le problème avec votre déclencheur actuel là où il ne traite pas actuellement correctement avec des inserts à plusieurs rangées.

(La paire DATEADD / DATEDIFF dans la définition de colonne calculée garantit simplement que toute date d'un mois est convertie au 1er de ce même mois, et constitue mon moyen généralement préféré d'ajuster les valeurs de date (heure) )


Pour faire cela à l'intérieur d'un déclencheur, vous devez vous rendre compte qu'un déclencheur FOR INSERT est également connu sous le nom de déclencheur AFTER. Vous trouverez toujours une ligne qui correspond à la ou aux lignes qui viennent d'être insérées car au moment où le déclencheur se déclenche, elles ont déjà été ajoutées à la table - les lignes se correspondent.

Normalement, si vous voulez empêcher l'insertion de lignes, je vous recommande d'utiliser un INSTEAD de déclencheur pour les empêcher d'apparaître dans la table finale. Cependant, cela peut devenir gênant - en particulier parce qu'il est facile d'oublier que les deux lignes en conflit peuvent ne pas être une ligne nouvellement insérée et une ligne existante, mais plutôt deux lignes nouvellement insérées.

Donc, nous allons nous en tenir au déclencheur FOR INSERT. Tout ce dont nous avons besoin est quelque chose comme:

ALTER TRIGGER [dbo].[CHECKCONSOMMATION]
ON [dbo].[ConsommationEau]
FOR INSERT
AS
    IF EXISTS(
         SELECT * FROM ConsommationEau WHERE
            idAbonnement IN (SELECT idAbonnement FROM inserted)
         GROUP BY idAbonnement,DATEADD(month,DATEDIFF(month,0,Periode),0)
         HAVING COUNT(*) > 1)
    BEGIN
      RAISERROR('THIS RECORD IS ALREADY EXISTS', 10, 1)
      ROLLBACK
    END

(Mais notez que cela a maintenant été transformé en un simple test d'unicité dans le tableau, qui, comme vous le constaterez, est plus clairement appliqué en utilisant les constructions conçues pour cela, telles qu'utilisées dans ma première solution ci-dessus)

4
Damien_The_Unbeliever 20 avril 2017 à 13:40

Vous devez utiliser un déclencheur INSTEAD OF pour insérer uniquement s'il ne correspond pas:

ALTER TRIGGER [dbo].[CHECKCONSOMMATION]
ON [dbo].[ConsommationEau]
Instead of INSERT
AS
    MERGE ConsommationEau c
    USING inserted i
    on c.idAbonnement = i.idAbonnement AND DATEDIFF(MONTH, c.Periode, i.Periode) = 0
    WHEN NOT MATCHED BY TARGET THEN
        INSERT ([idConsomationEau, [Periode], [Qte], [idAbonnement]) 
        VALUES (i.[idConsomationEau, i.[Periode], i.[Qte], i.[idAbonnement])
0
cloudsafe 20 avril 2017 à 14:13

Utilisez les mots-clés BEGIN et END après votre IF pour vous assurer que le ROLLBACK prend le IF in count.

IF EXISTS [condition]
BEGIN
    RAISERROR('Error',10,1)
    ROLLBACK
    RETURN
END

Mais surtout, je vous suggère d'utiliser une contrainte de vérification dans la définition du tableau:
https://stackoverflow.com/a/2570810/3635715

Référence Microsoft:
https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-column-constraint-transact-sql

1
Community 23 mai 2017 à 12:02