Mar*_*ary 2 trigger sql-server-2008-r2
我试图了解我的一个表 (DocumentDistribution) 上的现有触发器,它似乎是 DocumentInfo 和 DocumentSource 表之间的桥接表。我不明白这一行 (SET....FROM INSERTED) 是如何从中获取值的?这就是所谓的动态 SQL,它根据用户的选择从前端应用程序中获取该数据库的值吗?
我在尝试测试时遇到错误,因为子查询返回多行而语法期望单行,有什么建议吗?我正在使用 SQL Server 2008R2。
Alter TRIGGER [dbo].[DocDist_Dup_Check]
ON [dbo].[DocumentDistribution]
AFTER UPDATE, INSERT
AS
BEGIN
SET NOCOUNT ON;
SET ANSI_WARNINGS OFF;
DECLARE @docid int,
@sourceid int,
@errstr varchar(255);
SET @errstr = 'The distribution you have attempted to create already exists in the database.' + CHAR(13) +
'Duplicate distributions are not allowed for any document source except eBinder.';
SET @docid = (SELECT DocumentDistDocID FROM INSERTED);
SET @sourceid = (SELECT DocumentDistSourceID FROM INSERTED);
IF (SELECT COUNT(*) FROM DocumentDistribution WHERE DocumentDistDocID = @docid AND DocumentDistSourceID = @sourceid) > 1
AND
(@sourceid NOT IN(SELECT DocumentSourceID FROM DocumentSource WHERE DocumentSourceName = 'eBinder' OR DocumentSourceName = 'eBinder - West Division' OR DocumentSourceName = 'Consolidated e-Binder' OR DocumentSourceName = 'MA Consolidated eBinder'))
BEGIN
ROLLBACK TRANSACTION
END
ELSE
BEGIN
RETURN
END
Run Code Online (Sandbox Code Playgroud)
结尾
问题是触发器最初是用非常简单的逻辑和最少的测试编写的——它假设更新或插入一次只能影响一行。
您要问的那一行是尝试为可能包含多行的表中的变量分配一个值(SQL Server 中的触发器是按操作触发,而不是按行触发)。当有不止一行可供选择时,不同形式的变量赋值的行为会有所不同,例如:
DECLARE @foo TABLE(id INT);
INSERT @foo VALUES(1),(2);
DECLARE @id INT;
-- this will succeed, picking a single, may-as-well-be arbitrary row:
SELECT @id = id FROM @foo;
PRINT @id;
-- this will fail:
--SET @id = (SELECT id FROM @foo);
--PRINT @id;
Run Code Online (Sandbox Code Playgroud)
如果您运行第二个版本,您将收到一条您可能已经从触发器中看到的错误消息:
消息 512,级别 16,状态 1
子查询返回了 1 个以上的值。当子查询跟随 =、!=、<、<=、>、>= 或当子查询用作表达式时,这是不允许的。
并非所有都丢失了 - 您的触发器可以重新编写以处理受影响的多行 - 我将假设如果您插入两行并且其中只有一行失败,您希望整个事务回滚,即使另一排完全没问题。(此外,您的初始代码会填充@errstr
但从未使用过,因此我暂时将其排除在外。)
ALTER TRIGGER [dbo].[DocDist_Dup_Check]
ON [dbo].[DocumentDistribution]
AFTER UPDATE, INSERT
AS
BEGIN
SET NOCOUNT ON;
SET ANSI_WARNINGS OFF;
IF EXISTS
(
SELECT DocumentDistDocID, DocumentDistSourceID
FROM dbo.DocumentDistribution AS dd
WHERE EXISTS
(
SELECT 1 FROM inserted AS i
WHERE i.DocumentDistDocID = dd.DocumentDistDocID
AND i.DocumentDistSourceID = dd.DocumentDistSourceID
)
GROUP BY DocumentDistDocID, DocumentDistSourceID
HAVING COUNT(*) > 1
)
AND EXISTS
(
SELECT 1 FROM inserted
WHERE DocumentDistSourceID NOT IN
(
SELECT DocumentSourceID FROM dbo.DocumentSource
WHERE DocumentSourceName IN
('eBinder','eBinder - West Division',
'Consolidated e-Binder','MA Consolidated eBinder')
)
)
BEGIN
ROLLBACK TRANSACTION;
END
END
Run Code Online (Sandbox Code Playgroud)
我想我的逻辑是正确的;请在恢复的备份或开发系统上进行测试。
您可能会考虑将其实现为INSTEAD OF
触发器 - 这允许您根据业务逻辑插入或不插入(或仅插入不违反约束的行),而不是总是插入然后有时回滚。
如果此触发器的主要目的是防止重复,您还可以考虑实施唯一约束(或者可能是过滤的唯一索引或索引视图,如果只需要对子集强制执行重复项)。
归档时间: |
|
查看次数: |
349 次 |
最近记录: |