SQL Server 2017 触发器不遵守数据库设计并且其中的空值条件失败

SQL Server 2017 Triggers dont respect database design and null value conditions inside it fail

我在 SQL Server 2017 中有两个关于触发器的关键问题。它们是:

  1. 他们不尊重数据库设计。
  2. 触发器中的 NULL 值条件失败。

让我解释一下:

1.他们不尊重数据库设计: 假设我有 tables 和如下所示的视图,我正在尝试创建一个触发器,该触发器将根据已插入到视图中的内容插入到多个 tables 中。 (取自此 post - Instead of trigger to update view with multiple tables


CREATE TABLE persons
(personid  int, 
 firstname varchar(32) default 'Charles', 
 lastname  varchar(32));

CREATE TABLE employees
(employeeid int, 
 personid   int, 
 title      varchar(32));

CREATE VIEW vwEmployees AS
SELECT p.personid, employeeid, firstname, lastname, title
  FROM employees e JOIN persons p
    ON e.personid = p.personid;

CREATE TRIGGER tgEmployeesInsert ON vwEmployees
INSTEAD OF INSERT AS
BEGIN
  INSERT INTO persons (personid, firstname, lastname)
  SELECT personid, firstname, lastname
    FROM INSERTED

  INSERT INTO employees (employeeid, personid, title)
  SELECT employeeid, personid, title
    FROM INSERTED
END;

INSERT INTO vwEmployees (personid, employeeid, lastname, title)
VALUES(1, 1, 'Doe', 'SQL Developer');

如果我使用如下 select 语句:

select * from persons

这将获取如下结果:

personid firstname lastname
1 Doe

我不确定您是否注意到上面的内容,但是名字的默认值应该是 'Charles',如上面创建 table 中所述。

2。触发器中的 NULL 值条件失败。

在这里,我使用与上面相同的方法创建一个 instead of 触发器来更新具有多个 table 的视图。 假设我有一个如下所示的触发器:

CREATE or ALTER TRIGGER oc.trg_Update_Tracker ON oc.BP_Tracker
INSTEAD OF Update AS
BEGIN  
       Begin Try
       SET NOCOUNT ON
       Begin
            if update(Partner_ID) or update(Partner_Name) or update(LEI) 
            Begin
                Update oc.Business_Partner
                SET Partner_ID=ins.Partner_ID, Partner_Name=ins.Partner_Name, LEI=ins.LEI, User_Changed = ins.User_Changed
                from oc.Business_Partner bp, inserted as ins, deleted as del
                where bp.Partner_ID = del.Partner_ID and bp.Partner_Name = del.Partner_Name
            End
.........

假设 bp.Partner_Name 为空,del.Partner_Name 为空。在这种情况下,更新语句失败,因为条件 bp.Partner_Name = del.Partner_Name 在触发器中失败。如果要在 table 上的触发器外部传递相同的更新语句 - 那行得通。此外,如果我删除条件 bp.Partner_Name = del.Partner_Name,触发器更新将起作用。

如果有人能向我解释发生了什么,我将不胜感激。

我会把它放在评论中,但它不适合:

TL;DR:问题不在于触发器,而是你的理解有问题。

1:他们确实尊重数据库设计

DEFAULT 值得到尊重,您只是告诉 SQL 服务器不要使用它。让我们看看您的 INSERT 声明:

  INSERT INTO persons (personid, firstname, lastname)
  SELECT personid, firstname, lastname
    FROM INSERTED

因此,您将 inserted 伪 table 中的值 personidfirstnamelastname 插入到 table persons。这些列的值分别为 1NULL'Doe'。你有,在你的 INSERT 声明中明确表示你想 INSERTfirstname 的值放入同名列中,并且那个值是 NULL,所以 [=插入了 20=]。

如果你想要默认值,你应该在 INSERT 中完全省略 firstname,或者在 SELECT:

中声明 DEFAULT
  INSERT INTO persons (personid, lastname)
  SELECT personid, lastname
    FROM INSERTED;

  INSERT INTO persons (personid, firstname, lastname)
  SELECT personid, DEFAULT, lastname
    FROM INSERTED;

DEFAULT 约束 仅在 SELECT 中省略该列或传递 DEFAULT 选项时使用。 NULL 仍然是一个值,只是一个未知值,显式地将 NULL 插入列中不会导致使用 DEFAULT 值。

2:“触发器中的 NULL 值条件失败”

首先,我需要解决您的问题; 不再是 1989 年了。现在是 2022 年,ANSI-92 显式连接语法已经存在 30 年,你采用它的时间已经 long

但是,我无法测试此触发器是否具有针对它引用的对象省略了 DML 和 DDL,并且它自己的 DDL 不完整。不过,由于问题应限于 1 个问题,因此修复该触发器是针对单独的 new question.