sql - 如何在为链接表创建的视图的 INSTEAD OF UPDATE 触发器中更新
问题描述
我有表Group和Role,用外键链接。我需要通过视图更新它们,所以我必须编写 INSTEAD OF UPDATE 触发器。我目前的解决方案是错误的,我不知道如何解决它。我应该如何修复它?
表组
CREATE TABLE [Group]
(
groupId INT PRIMARY KEY IDENTITY,
groupName VARCHAR(50) NOT NULL,
picture IMAGE NULL,
CONSTRAINT UniqueGroupName UNIQUE (groupName)
)
表角色
CREATE TABLE Role
(
roleName VARCHAR(50),
groupId INT,
CONSTRAINT FK_Group FOREIGN KEY (groupId) REFERENCES [Group] (groupId),
PRIMARY KEY (roleName, groupId),
canBan TINYINT DEFAULT (0) /* 0 or 1 */
)
看法
CREATE VIEW GroupRoleView AS
SELECT g.groupName, r.roleName, r.canBan
FROM [Group] as g
INNER JOIN Role AS r ON g.groupId = r.groupId
GO
当前触发器,不正确
CREATE TRIGGER UpdateGroupRoleViewTrigger
ON GroupRoleView
INSTEAD OF UPDATE
AS
BEGIN
DECLARE @tempTable TABLE
(
groupId VARCHAR(50),
roleName VARCHAR(50),
oldBanAbility TINYINT,
newBanAbility TINYINT
)
INSERT INTO @tempTable (groupId, roleName, oldBanAbility, newBanAbility)
SELECT g.groupId, deleted.roleName, deleted.canBan, inserted.canBan
FROM deleted
INNER JOIN inserted ON deleted.groupName = inserted.groupName
LEFT JOIN [Group] as g on deleted.groupName = g.groupName
SELECT * FROM @tempTable
IF (UPDATE(roleName) OR UPDATE(groupName))
THROW 50003, 'You cannot move role between groups or change role name. Delete existing role and create a new one instead of this.', 1
IF (UPDATE(canBan))
BEGIN
--todo
UPDATE Role
SET canBan = (SELECT TOP 1 newBanAbility FROM @tempTable)
WHERE groupId IN (SELECT groupId FROM @tempTable)
AND roleName IN (SELECT roleName FROM @tempTable)
SELECT * FROM deleted
end
end
GO
解决方案
从表面上看,您陷入了假设触发器表中只有一行的经典陷阱。
另请注意,触发器忽略BEGIN
\END
以标记触发器的结束,因此您的批处理中之后的任何代码仍然包括在内。用 结束触发器GO
。
此外,如评论中所述,您没有检查值是否已更改。
在已删除/插入的查询中,您还缺少针对 roleName 的连接检查。
您的UPDATE
语句也没有正确连接,在这种情况下,您根本不需要临时表。
这是正确的版本:
CREATE TRIGGER UpdateGroupRoleViewTrigger
ON GroupRoleView
INSTEAD OF UPDATE
AS
IF (NOT EXISTS (SELECT 1 FROM inserted)) -- early bail-out
RETURN;
SET NOCOUNT ON; -- prevent confusion when checking rows updated in client
IF (EXISTS (
SELECT roleName, groupName FROM inserted
EXCEPT
SELECT roleName, groupName FROM deleted))
THROW 50003, 'You cannot move role between groups or change role name. Delete existing role and create a new one instead of this.', 1;
IF (UPDATE(canBan))
BEGIN
--todo
UPDATE r
SET canBan = i.canBan
FROM deleted d
-- added rolename equi-join as that appears to be correct from the schema
INNER JOIN inserted i ON d.groupName = i.groupName AND d.roleName = i.roleName
AND d.canBan <> i.canBan
INNER JOIN Role r ON r.groupName = i.groupName AND r.roleName = i.roleName
END;
GO
然而...
SQL Server 实际上支持可更新视图,因此您实际上根本不需要触发器。只要所有列都在一个表中,您就应该能够直接更新视图。
推荐阅读
- python - python读取文本文件并添加字符串并创建新的txt文件
- rest - 使用 JSON REST API 时如何在 Jenkins 中过滤具有作业名称的作业?
- angular - 双击内联编辑页面导航问题
- python - 如何在 NEST 模拟器中设置 Connection 的突触类型?
- javascript - 使用异步/回调来获取数据?
- python - 解包时如何检查字典列表中是否存在密钥
- python - 根据相邻列中的 id 号和 sting 值解析 pandas 数据帧
- c++ - C++匿名字符串销毁时间
- typescript - 如何使用 jsDoc 在自定义组件中记录此关键字?
- javascript - 内容安全策略阻止了 less.min.js 的资源