Sql server 使用触发器的sp_addlinkserver
我有以下触发器,它在运行时会导致错误:Sql server 使用触发器的sp_addlinkserver,sql-server,triggers,linked-server,Sql Server,Triggers,Linked Server,我有以下触发器,它在运行时会导致错误: CREATE TRIGGER ... ON ... FOR INSERT, UPDATE AS IF UPDATE(STATUS) BEGIN DECLARE @newPrice VARCHAR(50) DECLARE @FILENAME VARCHAR(50) DECLARE @server VARCHAR(50) DECLARE @provider VARCHAR(50) DECLARE @datasr
CREATE TRIGGER ...
ON ...
FOR INSERT, UPDATE
AS
IF UPDATE(STATUS)
BEGIN
DECLARE @newPrice VARCHAR(50)
DECLARE @FILENAME VARCHAR(50)
DECLARE @server VARCHAR(50)
DECLARE @provider VARCHAR(50)
DECLARE @datasrc VARCHAR(50)
DECLARE @location VARCHAR(50)
DECLARE @provstr VARCHAR(50)
DECLARE @catalog VARCHAR(50)
DECLARE @DBNAME VARCHAR(50)
SET @server=xx
SET @provider=xx
SET @datasrc=xx
SET @provstr='DRIVER={SQL Server};SERVER=xxxxxxxx;UID=xx;PWD=xx;'
SET @DBNAME='[xx]'
SET @newPrice = (SELECT STATUS FROM Inserted)
SET @FILENAME = (SELECT INPUT_XML_FILE_NAME FROM Inserted)
IF @newPrice = 'FAIL'
BEGIN
EXEC master.dbo.sp_addlinkedserver
@server, '', @provider, @datasrc, @provstr
EXEC master.dbo.sp_addlinkedsrvlogin @server, 'true'
INSERT INTO [@server].[@DBNAME].[dbo].[maildetails]
(
'to', 'cc', 'from', 'subject', 'body', 'status',
'Attachment', 'APPLICATION', 'ID', 'Timestamp', 'AttachmentName'
)
VALUES
(
'P23741', '', '', 'XMLFAILED', @FILENAME, '4',
'', '8', '', GETDATE(), ''
)
EXEC sp_dropserver @server
END
END
错误是:
Msg 15002,16级,状态1,程序sp_MSaddserver_内部,第28行
无法在事务中执行过程“sys.sp_addlinkedserver”。
Msg 15002,16级,状态1,程序sp_addlinkedsrvlogin,第17行
无法在事务中执行过程“sys.sp_addlinkedsrvlogin”。
Msg 15002,16级,状态1,程序sp_dropserver,第12行
无法在事务中执行过程“sys.sp_dropserver”
如何防止发生此错误?错误明确指出,您不能在事务中添加链接服务器。触发器作为隐式事务运行,因此,如果触发器执行任何验证且验证失败,则可以
回滚
我真的无法想象你为什么要这么做。除了一些非常罕见的例外情况(我不想在这里提及),链接服务器作为临时设备并不理想。只需永久添加一次链接服务器,就不会出现此问题。您还可以将其管理和安全性视为管理功能,而不是在某个地方的脚本中硬编码。如果您希望动态访问远程服务器,请使用OPENROWSET。我总是这么做。你的扳机在很多方面都不好。首先,不能在触发器中添加链接服务器。您应该以管理方式添加它一次,而不必再担心它
接下来,也是非常重要的一点,即使链接服务器不存在,触发器也只能在插入和更新一行时工作,如果发生多行插入/更新,则触发器将无法正常工作。这是触发器设计的第一条、最基本的规则,永远不要假设只处理一行。每当我看到使用了values子句,或者将inserted或deleted中的值设置为变量时,我就知道触发器坏了,需要重写。现在这似乎是在发送一封电子邮件,如果更新了那么多记录,你真的想发送1907898封电子邮件吗?还是只有一封?如果只需要一个ID,则需要一种方法来识别所有受影响的ID。如果有人需要更新一整套价格,并通过基于集合的更新状态进行更新,而不是手动通过用户界面一次更新10000个价格,那么您真正希望发生什么?不要说只有一条记录会被更新或插入。迟早会有人需要进行批量插入或更新,而你的触发器会悄悄地导致错误的事情发生。你甚至不会知道它失败了,因为它不会出错,它根本不会做你需要它做的事情。这是一场噩梦,无法修复,数据完整性问题是如何发生的
另一件事是,db的写入方式不会改变,因此您不再需要删除链接服务器内容的变量。否则,作为wrttten,您将不得不更改为动态sql以使其正常工作,而这在触发器(或通常在其他任何地方)中是一个糟糕的主意,因为它不会发生变化,所以根本没有理由使用它
基于集合的解决方案(假设插入或更新的每个项目都需要一条记录,并且假设设置了永久链接服务器):
我最后要提醒您的是,如果链接服务器因任何原因停机,即使这样也会失败。这意味着当它关闭时,不能在表中添加或更改任何记录。在把它放入触发器之前仔细考虑这个问题。< /P>永久链接服务器是服务器级对象。对于我们这些与其他开发团队共享服务器的人来说,这是与那些开发团队的又一个可以避免的协调点。如果使用自动生成工具(如NAnt)跨环境协调DDL更改的部署,则需要在数据库级别或更低级别上提供尽可能多的内容。@entaroadun:另一方面,OPENROWSET
既需要源服务器上的特殊权限,也需要保存到目标服务器的凭据。这对于开发来说可能不是问题,但许多团队可能不希望在生产中使用它。
INSERT INTO myserver].mydatabase.[dbo].[maildetails]
(
'to', 'cc', 'from', 'subject', 'body', 'status',
'Attachment', 'APPLICATION', 'ID', 'Timestamp', 'AttachmentName'
SELECT 'P23741', '', '', 'XMLFAILED', INPUT_XML_FILE_NAME , '4',
'', '8', '', GETDATE(), ''
FROM inserted
WHERE status = 'Fail'