Sql 创建触发器以检查记录是否存在
我想创建一个触发器来检查插入之前是否存在记录,如果存在则回滚,如果不存在则继续执行插入。 问题是当我插入时,它总是回滚。 我该怎么办Sql 创建触发器以检查记录是否存在,sql,sql-server,Sql,Sql Server,我想创建一个触发器来检查插入之前是否存在记录,如果存在则回滚,如果不存在则继续执行插入。 问题是当我插入时,它总是回滚。 我该怎么办 ALTER TRIGGER [dbo].[CHECKCONSOMMATION] ON [dbo].[ConsommationEau] FOR INSERT AS DECLARE @IDABONNEMENT INT DECLARE @DEFMONTH DATETIME SELECT @IDABONNEMENT = inserted.idAbonnement FRO
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
这是我的桌子
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
在IF之后使用关键字BEGIN和END,以确保回滚计数中包含IF
IF EXISTS [condition]
BEGIN
RAISERROR('Error',10,1)
ROLLBACK
RETURN
END
但最重要的是,我建议您在表定义中使用检查约束:Microsoft Reference:
在IF之后使用关键字BEGIN和END,以确保回滚将IF计入计数
IF EXISTS [condition]
BEGIN
RAISERROR('Error',10,1)
ROLLBACK
RETURN
END
但最重要的是,我建议您在表定义中使用检查约束:Microsoft Reference:
如果目的是表每月只能包含一行,那么我不会使用触发器。我将使用持久化计算列和唯一索引:
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)
通过这种方式,您声明了什么应该是唯一的,而不必编写过程代码,并且避免了当前触发器当前无法正确处理多行插入的问题
(计算列定义中的DATEADD
/DATEDIFF
对仅确保一个月内的任何日期转换为同一个月的第一个日期,这是我通常首选的调整日期(时间)值的方法)
要在触发器内部执行此操作,您必须意识到插入的
触发器也称为后触发器。您将始终找到与刚刚插入的行匹配的行,因为在触发触发器时,它们已经被添加到表中-这些行正在匹配它们自己
通常,如果要防止插入行,我建议您使用触发器的代替,以防止它们出现在最终表中。但是,这可能会变得很尴尬——特别是因为很容易忘记,两个冲突的行可能不是新插入的行和现有的行,而是两个新插入的行
因此,我们将继续使用INSERT触发器的。我们所需要的是:
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
(但请注意,这已经转化为表内的一个简单的唯一性测试,正如您将观察到的,使用为此设计的结构(如上面我的第一个解决方案中所使用的)可以更灵活地执行该测试)如果目的是表每月只能包含一行,那我就不用扳机了。我将使用持久化计算列和唯一索引:
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)
通过这种方式,您声明了什么应该是唯一的,而不必编写过程代码,并且避免了当前触发器当前无法正确处理多行插入的问题
(计算列定义中的DATEADD
/DATEDIFF
对仅确保一个月内的任何日期转换为同一个月的第一个日期,这是我通常首选的调整日期(时间)值的方法)
要在触发器内部执行此操作,您必须意识到插入的触发器也称为后触发器。您将始终找到与刚刚插入的行匹配的行,因为在触发触发器时,它们已经被添加到表中-这些行正在匹配它们自己
通常,如果要防止插入行,我建议您使用触发器的代替,以防止它们出现在最终表中。但是,这可能会变得很尴尬——特别是因为很容易忘记,两个冲突的行可能不是新插入的行和现有的行,而是两个新插入的行
因此,我们将继续使用INSERT触发器的。我们所需要的是:
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
(但请注意,这已经转化为表内的一个简单的唯一性测试,正如您将观察到的,使用为此设计的结构(如上面我的第一个解决方案中所使用的)可以更灵活地执行该测试)您应该使用INSTEAD OF触发器,仅在不匹配时插入:
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])
只有在不匹配时,才应使用INSTEAD OF触发器进行插入:
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])
你能澄清确切的要求吗?看起来您试图将表限制为每月仅一行。对吗?(顺便说一句,您当前的触发器已严重损坏-inserted
可以包含多行。在这种情况下,您无法保证@IDABONNEMENT
和@DEFMONTH
甚至来自同一行,更不用说正确处理这些多行了)是的,确切地说,我想将表限制为每个IDABONNEMENT只有一行,每月。你能说明确切的要求吗?看起来您试图将表限制为每月仅一行。对吗?(顺便说一句,您当前的触发器已严重损坏-inserted
可以包含多行。在这种情况下,您无法保证@IDABONNEMENT
和@DEFMONTH
甚至来自同一行,更不用说正确处理这些多行了)是的,确切地说,我想将表限制为每个IDABONNEMENT只有一行,每个月。是的,我在发布问题之前做了,但我没有工作。查看我对约束定义的编辑,我认为这些约束定义将应用于您试图实现的目标(使用触发器有点“脏”),谢谢您的帮助,我会调查它。是的,我在发布问题之前做了,但是我没有工作。请看我对约束定义的编辑,这些约束定义对我来说将适用于您试图实现的目标(使用触发器有点“脏”),谢谢您的帮助,我会研究它。我在这里没有选择,我已经告诉您使用触发器,这周来一直在寻找正确的答案。@hyungsik-我已经做了一个触发器版本,但我真的不推荐它。你实际上是个顽皮鬼