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'