SQL Server:使用INSTEAD OF INSERT触发器插入表中

时间:2017-06-22 22:18:37

标签: sql-server tsql triggers sql-server-2016

这个Microsoft Documentation表示“关于引用具有INSTEAD OF触发器的表的INSERT或UPDATE语句何时必须为列提供值的规则与表没有INSTEAD OF触发器的情况相同”。

然而:

CREATE TABLE tbTriggerTest (id INT PRIMARY KEY);
GO

CREATE TRIGGER dbo.tgTriggerTest
ON dbo.tbTriggerTest
INSTEAD OF INSERT
BEGIN
    SELECT * FROM INSERTED
END
GO

INSERT tbTriggerTest DEFAULT VALUES
GO
即使在INSERT语句级别我正在向主键中插入空值,

也不会抛出错误。事实上,在这种情况下INSERTED.id为NULL。那么文档是不正确的还是它只适用于触发器内?

编辑:响应建议的答案,声明调用者必须为非空列提供一些值,即使它为null,以下内容也不会引发错误,但可能无法提供任何值列ij

CREATE TABLE tbTriggerTest2
(
    id INT,
    ij INT,
    CONSTRAINT PK PRIMARY KEY (id, ij)
)
GO

CREATE TRIGGER dbo.tgTriggerTest2
   ON  dbo.tbTriggerTest2
   INSTEAD OF INSERT
AS 
BEGIN
    SELECT * FROM INSERTED;
END
GO

INSERT tbTriggerTest2 (id) VALUES (0)
GO

1 个答案:

答案 0 :(得分:0)

嗯,您引用的docs说:

  

以下有关INSERT或UPDATE语句的规则   引用具有INSTEAD OF触发器的表必须为其提供值   列与表没有INSTEAD OF的情况相同   触发:

     
      
  • INSERT语句必须为没有DEFAULT约束的所有NOT NULL列提供值。
  •   

问题中的INSERT声明

INSERT tbTriggerTest DEFAULT VALUES

为所有列提供值。它不会省略任何列。是的,它提供NULL个值,但文档并未说明INSERT语句应提供非null 值。它说INSERT语句应该提到所有非空列。这就是解析器检查的内容 - 列列表。

如果违反INSERT约束,实际NOT NULL操作将失败。在您的示例中,实际的INSERT永远不会发生,因此您不会看到任何错误。

就像你在桌面上没有任何触发器并尝试运行

一样
INSERT tbTriggerTest VALUES (NULL)

服务器将解析该语句,看到VALUES子句与表的列列相同,并尝试执行INSERT。它将因约束违规而失败。

  

无法将值NULL插入列'id',表中   'AdventureWorks2014.dbo.tbTriggerTest';列不允许空值。   INSERT失败。声明已经终止。

您可以尝试获得估算的执行计划,然后您将获得一个计划。引擎生成了一个计划并开始执行它。

如果您尝试运行

INSERT tbTriggerTest VALUES (NULL, NULL)

如果没有尝试执行,该语句将失败。

  

列名或提供的值数与表不匹配   定义

如果你试图获得一个估计的执行计划,你就不会得到一个。引擎无法生成计划,因为列的列表不匹配。

第二个例子

在第二个示例中,idij列都可以为空。因此,再次,通过触发器或没有触发器,引擎将仅接受具有一个值的INSERT语句。第二个值将被假定为NULL

如果VALUES子句的列数少于表中的列数,则引擎(或更确切地说,DEFAULT)假定缺少的列为NULL。因此,在您的示例中,表有两列,语句

INSERT tbTriggerTest2 (id) VALUES (0)

与:

相同
INSERT tbTriggerTest2 (id, ij) VALUES (0, NULL)

第二列可以为空,因此它具有默认的NULL值。您可以指定列的其他默认值,如果您不在列列表中包含此列,INSERT也将按预期工作。

如果您将表的定义更改为:

CREATE TABLE tbTriggerTest2
(
    id INT NOT NULL,
    ij INT NOT NULL,
    CONSTRAINT PK PRIMARY KEY (id, ij)
)

然后没有触发器,以下语句将失败:

INSERT tbTriggerTest2 (id) VALUES (0)
  

无法将值NULL插入列'ij',表中   'dbo.tbTriggerTest2';列不允许空值。 INSERT失败。该   声明已被终止。

使用触发器,整个语句将成功运行,因为触发器会禁止尝试将具有NULL值的行插入到不接受NULL的列中。

如果您将表的定义更改为:

CREATE TABLE tbTriggerTest3
(
    id INT NOT NULL,
    ij INT NOT NULL DEFAULT 10
)

然后

INSERT tbTriggerTest3 (id) VALUES (0)

的工作原理如下:

INSERT tbTriggerTest3 (id, ij) VALUES (0, 10)

无论是否触发,它都会成功。

另一方面,

INSERT tbTriggerTest3 (id) VALUES (NULL)

与:

相同
INSERT tbTriggerTest3 (id, ij) VALUES (NULL, 10)

如果没有触发器,它将会失败

  

无法将值NULL插入列'id',表中   'dbo.tbTriggerTest3';列不允许空值。 INSERT失败。

,因为它会尝试将NULL插入id,但不接受NULL

使用禁止插入行的触发器,它将成功。