sp_addlinkserver используя триггер - PullRequest
2 голосов
/ 31 октября 2009

У меня есть следующий триггер, который вызывает ошибку при запуске:

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

Ошибка:

Сообщение 15002, уровень 16, состояние 1, процедура sp_MSaddserver_internal, строка 28 Процедура sys.sp_addlinkedserver не может быть выполнена внутри транзакции. Сообщение 15002, уровень 16, состояние 1, процедура sp_addlinkedsrvlogin, строка 17 Процедура sys.sp_addlinkedsrvlogin не может быть выполнена внутри транзакции. Сообщение 15002, уровень 16, состояние 1, процедура sp_dropserver, строка 12 Процедура sys.sp_dropserver не может быть выполнена внутри транзакции.

Как я могу предотвратить возникновение этой ошибки?

Ответы [ 3 ]

1 голос
/ 01 марта 2010

Используйте OPENROWSET, если вы хотите подключиться к удаленному серверу динамически. Я делаю это все время.

1 голос
/ 01 марта 2010

Ваш триггер плох во многих отношениях. Во-первых, вы не можете добавить связанный сервер в триггер. Вы должны добавить это один раз в административном порядке и не беспокоиться об этом снова.

Следующее и чрезвычайно важное значение, даже при отсутствии связанного сервера, ваш триггер будет работать только в том случае, если вставлена ​​и обновлена ​​одна строка, и не будет работать должным образом, если происходит вставка / обновление нескольких строк. Это первое, самое основное правило разработки триггеров, никогда не предполагайте, что обрабатывается только одна строка. Каждый раз, когда я вижу использованное предложение values ​​или значение из вставленной или удаленной установки в переменную, я знаю, что триггер неверный и его нужно переписать. Похоже, это отправляет электронное письмо. Вы действительно хотите отправить 1907898 электронных писем, если было обновлено столько записей или только одна? Если вы хотите только один, вам нужен способ идентифицировать все затронутые идентификаторы. Что вы на самом деле хотите, чтобы произошло, если кто-то должен обновить целую кучу цен и делает это через основанную на множестве статистику обновления, а не вручную через пользовательский интерфейс по одному за 10 000 цен? И не говорите, что только одна запись когда-либо будет обновлена ​​или вставлена. Рано или поздно кому-то понадобится выполнить пакетную вставку или обновление, и ваш триггер тихо вызовет ошибку. Вы даже не узнаете, что это не удалось, потому что это не приведет к ошибке, просто не будет делать то, что вам нужно. Вот почему возникают кошмары, которые невозможно исправить, проблемы с целостностью данных.

Еще одна вещь, то, как написано это, БД не изменится, поэтому вам больше не нужна переменная со снятым содержимым связанного сервера. В противном случае, как wrttten, вам придется перейти на динамический sql, чтобы заставить это работать должным образом, и это плохая идея в триггере (или где-то еще обычно), и так как он не будет меняться, нет никаких причин использовать его.

Основанное на множестве решение (при условии, что вы хотите одну запись для каждого элемента, вставленного или обновленного, и при условии, что вы настроили постоянный связанный сервер):

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'

Последнее, что я вам предупреждаю, это то, что даже это не удастся, если связанный сервер по какой-либо причине выйдет из строя. Это означает, что пока он не работает, никакие записи не могут быть добавлены или изменены в таблице. Обдумайте это очень внимательно, прежде чем включать это в триггер.

1 голос
/ 24 февраля 2010

Как ясно указывает на ошибку, вы не можете добавить связанный сервер в транзакции. Триггеры выполняются как неявные транзакции, поэтому вы можете ROLLBACK, если триггер выполняет какую-либо проверку и эта проверка завершается неудачей.

Я действительно не могу себе представить, почему вы все равно хотите это сделать. За редкими исключениями, о которых я не буду здесь упоминать, связанный сервер не идеален в качестве временного устройства. Просто добавьте связанный сервер один раз, навсегда, тогда у вас не будет этой проблемы. Вы также сможете рассматривать его управление и безопасность как административную функцию, а не жестко кодировать ее где-то в сценарии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...