web-dev-qa-db-ja.com

トリガーからのロールバックトランザクション

MS SQL Server 2008 R2では、何かをチェックし、実行中の挿入/更新を許可またはロールバックする(raiserrorを介して)事前挿入および事前更新トリガーが必要です。

質問INSTEAD OF 引き金。本当に挿入または更新を明示的に記述する必要がありますか?デフォルトの挿入または更新を実行し、「事前チェック」のみを実行する必要があるためです。

9
Cartesius00

はい。

明示的なINSERTまたはUPDATEを記述する必要があります。

トリガーは_INSTEAD OF_ DML操作を実行します。トリガーを空白のままにすると、INSERTED/DELETEDテーブルが作成されてtempdbに入力される以外のアクションは発生しません。

コメントでの議論から、私はこれにトリガーをまったく使用しませんが、一意のフィルター処理されたインデックスCREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> ''を使用します。これにより、パフォーマンスが向上し、並行性を処理する際の潜在的なロジックの問題が回避される可能性があります。

10
Martin Smith

実際の挿入または更新を置き換える場合を除いて、INSTEAD OFトリガーはおそらく必要ありません。あなたの場合、代わりにFOR INSERT, UPDATEトリガーが必要です。

このトリガーの例では、誰かがtitlesテーブルのデータを追加または変更しようとすると、クライアントにメッセージを出力します。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
   DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE 
AS RAISERROR ('inserts and updates to the titles table is not allowed', 16, 1)
GO

IF EXISTSCOLUMNS_UPDATEDなどを使用することもできます。

ここでは、ロールバックを使用する別の例を示します。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'employee_insupd' AND type = 'TR')
   DROP TRIGGER employee_insupd
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
   @max_lvl tinyint,
   @emp_lvl tinyint,
   @job_id smallint
SELECT @min_lvl = min_lvl, 
   @max_lvl = max_lvl, 
   @emp_lvl = i.job_lvl,
   @job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id 
   JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl <> 10) 
BEGIN
   RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
   ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
   RAISERROR ('The level for job_id:%d should be between %d and %d.',
      16, 1, @job_id, @min_lvl, @max_lvl)
   ROLLBACK TRANSACTION
END

取引があるかどうかはわかりませんが、あなたの場合は次のようなものが必要です。

USE myDatabase

    IF EXISTS (SELECT name FROM sysobjects
          WHERE name = 'myTable' AND type = 'TR')
       DROP TRIGGER tr_myTrigger
    GO
    CREATE TRIGGER tr_myTrigger
    ON myTable
    FOR INSERT, UPDATE
    AS

if(exists(select * from inserted where rtrim(c) <> ''))
begin
  -- check to make sure the insert(s) are unique

    if(exists(
      select * from inserted i
      join dbo.myTable t on i.a = t.a and i.b = t.b and i.c = t.c)

    begin
      raiserror('Duplicate(s) found', 16, 1)
      rollback transaction
    end
end
3
Chris Gessler