Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 创建触发器以检查记录是否存在_Sql_Sql Server - Fatal编程技术网

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-我已经做了一个触发器版本,但我真的不推荐它。你实际上是个顽皮鬼