SQL Server 无法插入启用触发器的表中

Posted

技术标签:

【中文标题】SQL Server 无法插入启用触发器的表中【英文标题】:SQL Server cant insert into table with trigger enabled 【发布时间】:2019-10-22 14:46:40 【问题描述】:

我有一个表,其中我只能有 2 行状态为激活 (1),所以我有一个可以限制它的触发器。

这是触发器:

ALTER TRIGGER [dbo].[ciclo_OI] 
ON [dbo].[ciclo] 
FOR INSERT 
AS 
BEGIN
    SET NOCOUNT ON

    IF (SELECT COUNT(*) FROM ciclo WHERE cicloEstado = 1) > 2
       ROLLBACK TRANSACTION
END
GO

问题是当我尝试在表格中插入一些东西时,我得到了这个错误:

消息 334,第 16 级,状态 1,第 1 行 如果 DML 语句的目标表 'ciclo' 包含没有 INTO 子句的 OUTPUT 子句,则该语句不能有任何启用的触发器。

我能做些什么来解决这个问题?

【问题讨论】:

你能显示导致错误的插入语句吗? 如果您要回滚事务,通知插入这些行的过程不是很好吗?你知道 - 所以它至少可以捕获(并可能响应)错误? 而且报错信息很清楚。不要在插入语句中使用输出子句,在输出子句中使用 into 选项,或者更改捕获错误的方法。 larnu 的演示和讨论here 【参考方案1】:

不要为此使用触发器。使用过滤的唯一索引会更好:

CREATE UNIQUE INDEX UQ_one_cicloEstado
    ON dbo.ciclo_OI (cicloEstado)
    WHERE cicloEstado = 1;

例子:

CREATE TABLE dbo.TestTable (ID int IDENTITY,
                            SomeInt int,
                            SomeString varchar(10));

CREATE UNIQUE INDEX UQ_one_SomeInt
    ON dbo.TestTable (SomeInt)
    WHERE SomeInt = 1;
GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(1,'asdkasd'); --Works. 
GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(2,'asdfgdf'); --Works.

GO
INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(1,'sdfsdf'); --Fails.

GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(2,'etrytrg'); --Works.
GO
SELECT *
FROM dbo.TestTable;

GO
DROP TABLE dbo.TestTable;

由于 OP 实际上想要 2 行,因此上述内容不正确,但我已将其留在那里。

在我看来,触发器仍然不是最好的地方,但是(不幸的是)它只给我们留下了一个多行标量函数。远非理想,this 可能会受到竞争条件的影响,但我怀疑(由于“2 规则”)这不太可能。这是一个例子:

CREATE TABLE dbo.TestTable (ID int IDENTITY,
                            SomeInt int,
                            SomeString varchar(10));



GO

CREATE FUNCTION dbo.CheckInt1Count()
RETURNS INT
AS BEGIN

    DECLARE @Count int = 0;
    SELECT @Count = COUNT(*)
    FROM dbo.TestTable
    WHERE SomeInt = 1;

    RETURN @Count;
END;
GO

ALTER TABLE dbo.TestTable ADD CONSTRAINT ck_int1Count CHECK (dbo.CheckInt1Count() <= 2);
GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(1,'asdkasd'); --Works. 
GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(2,'asdfgdf'); --Works.

GO
INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(1,'sdfsdf'); --Works.

GO

INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(2,'etrytrg'); --Works.
GO
INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(1,'jdgfhbsk'); --Fails.
GO
GO
INSERT INTO dbo.TestTable (SomeInt,
                           SomeString)
VALUES(2,'sdfipasdf'); --Works.

GO
SELECT *
FROM dbo.TestTable;
GO

DROP TABLE TestTable;
DROP FUNCTION dbo.CheckInt1Count;

【讨论】:

我用过,但问题是可以同时激活两条记录(1),所以我不能使用唯一索引 为你添加了一个不同的想法,@ArielPenayo。【参考方案2】:

我建议将 count(*) 的结果放入一个变量中,然后查询该变量。

【讨论】:

以上是关于SQL Server 无法插入启用触发器的表中的主要内容,如果未能解决你的问题,请参考以下文章

sql插入的操作

SQL Server 在多个线程中插入死锁

SQL Server 怎么用存触发器实现从一个表里查询数据,然后插入到另一个表里

SQL Server 触发器

SQL Server 触发器:在区分大小写的数据库中“插入”的对象名称无效

SQL SERVER 触发器