2

次のトリガーがあり、実行時にエラーが発生します。

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' はトランザクション内で実行できません。

このエラーの発生を防ぐにはどうすればよいですか?

4

3 に答える 3

1

あなたの引き金は多くの点で悪いです。まず、リンクサーバーをトリガーに追加することはできません。管理上一度追加する必要があり、再度心配する必要はありません。

次に、非常に重要なことですが、リンクサーバーがなくなった場合でも、トリガーは1つの行が挿入および更新された場合にのみ機能し、複数の行の挿入/更新が発生した場合は正しく機能しません。これは、トリガー設計の最初の最も基本的なルールであり、1つの行だけが処理されるとは決して想定していません。使用されているvalues句、または挿入または削除された値が変数に設定されているのを見ると、トリガーが不良であり、書き直す必要があることがわかります。これでメールが送信されるように見えますが、その数のレコードが更新された場合、または1つだけの場合、本当に1907898のメールを送信しますか?1つだけが必要な場合は、影響を受けるすべてのIDを識別する方法が必要です。誰かが大量の価格を更新する必要があり、ユーザーインターフェイスを一度に1つずつ手動で10回実行するのではなく、セットベースの更新ステートメントを使用して更新する必要がある場合、本当に何をしたいですか。000の価格?また、1つのレコードが更新または挿入されるとは言わないでください。遅かれ早かれ、誰かがバッチ挿入または更新を行う必要があり、トリガーによってサイレントに間違ったことが起こります。エラーが発生しないため、失敗したことすらわかりません。必要なことを実行しないだけです。これが悪夢であり、修正が不可能であり、データの整合性の問題が発生する方法です。

もう1つ、これの記述方法はデータベースに変更されないため、リンクされたサーバーのものを削除しても変数は不要になります。それ以外の場合は、これを正しく機能させるために動的SQLに変更する必要があります。これは、トリガー(または通常は他の場所)ではお勧めできません。変更されないため、使用する理由はまったくありません。

セットベースのソリューション(挿入または更新されるアイテムごとに1つのレコードが必要であり、永続的なリンクサーバーをセットアップすると仮定):

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'

最後に注意しなければならないのは、リンクサーバーが何らかの理由でダウンした場合、これでも失敗するということです。これは、ダウンしている間は、テーブルでレコードを追加または変更できないことを意味します。これをトリガーに入れる前に、これを非常に慎重に検討してください。

于 2010-03-01T20:23:37.513 に答える
1

リモート サーバーに動的にアクセスする場合は、OPENROWSET を使用します。私はいつもそれをします。

于 2010-03-01T20:00:59.407 に答える
1

エラーが明確に示しているように、トランザクション内にリンク サーバーを追加することはできません。トリガーは暗黙的なトランザクションとして実行されるためROLLBACK、トリガーが検証を実行し、その検証が失敗した場合に実行できます。

とにかく、なぜあなたがこれをやりたいのか、私には本当に想像できません。ここではあえて言及しない非常にまれな例外をいくつか除いて、リンク サーバーは一時的な備品としては理想的ではありません。リンクされたサーバーを一度、永続的に追加するだけで、この問題は発生しません。また、その管理とセキュリティをスクリプトにハードコーディングするのではなく、管理機能として扱うこともできます。

于 2010-02-23T22:29:00.203 に答える