SQL Server 2017 触发器不遵守数据库设计并且其中的空值条件失败
SQL Server 2017 Triggers dont respect database design and null value conditions inside it fail
我在 SQL Server 2017 中有两个关于触发器的关键问题。它们是:
- 他们不尊重数据库设计。
- 触发器中的 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 中的值 personid
、firstname
和 lastname
插入到 table persons
。这些列的值分别为 1
、NULL
和 'Doe'
。你有,在你的 INSERT
声明中明确表示你想 INSERT
将 firstname
的值放入同名列中,并且那个值是 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.
我在 SQL Server 2017 中有两个关于触发器的关键问题。它们是:
- 他们不尊重数据库设计。
- 触发器中的 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 中的值 personid
、firstname
和 lastname
插入到 table persons
。这些列的值分别为 1
、NULL
和 'Doe'
。你有,在你的 INSERT
声明中明确表示你想 INSERT
将 firstname
的值放入同名列中,并且那个值是 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.