2009-10-31 5 views
2

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

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_internal, линия 28 Процедура 'sys.sp_addlinkedserver' не может быть выполнена в транзакции. Msg 15002, уровень 16, состояние 1, процедура sp_addlinkedsrvlogin, строка 17 Процедура 'sys.sp_addlinkedsrvlogin' не может быть выполнена в транзакции. Msg 15002, уровень 16, состояние 1, процедура sp_dropserver, строка 12 Процедура 'sys.sp_dropserver' не может быть выполнена в транзакции.

Как предотвратить эту ошибку?

ответ

1

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

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

+0

Постоянно связанные серверы являются объектами уровня сервера. Для тех из нас, кто использует серверы с другими командами разработчиков, это еще одна координационная точка с этими командами разработчиков, которых можно избежать. Если вы используете автоматические инструменты сборки, например NAnt, для координации развертывания изменений DDL в среде, вы хотите как можно больше материала на уровне базы данных или ниже. – alyssackwan

+0

@entaroadun: обратная сторона заключается в том, что 'OPENROWSET' требует как специальных разрешений на исходном сервере, так и сохраненных учетных данных для адресата. Это, вероятно, не проблема для разработки, но многие команды могут не захотеть этого в производстве. – Aaronaught

1

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

1

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

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

Другое дело, так, как это записано, db не изменится, поэтому вам больше не понадобится переменная с удаленным соединенным файлом сервера. В противном случае, как 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' 

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

Смежные вопросы