2013-10-19 5 views
5

ための代わり-のINSERTトリガー、私は、次のデータベース・スキーマを持つようにしたい:ロギングシステムのために複数のテーブル

CREATE TABLE [dbo].[Categories] (
    [Id]  INT   IDENTITY (1, 1) NOT NULL, 
    [App]  NVARCHAR (30) NULL, 
    [Source] NVARCHAR (30) NULL, 
    [LogLevel] NVARCHAR (5) NULL, 
    [Logger] NVARCHAR (120) NULL, 
    CONSTRAINT [PK_Categories] PRIMARY KEY NONCLUSTERED ([Id] ASC), 
    CONSTRAINT [UK_Categories] UNIQUE NONCLUSTERED ([App] ASC, [Source] ASC, [LogLevel] ASC, [Logger] ASC) 
); 

CREATE TABLE [dbo].[Occurences] (
    [PointInTime] BIGINT NOT NULL, 
    [CategoryId] INT NOT NULL, 
    [Noise]  INT NOT NULL, 
    CONSTRAINT [PK_Occurences] PRIMARY KEY CLUSTERED ([PointInTime] ASC, [CategoryId] ASC, [Noise] ASC), 
    CONSTRAINT [FK_Category] FOREIGN KEY ([CategoryId]) REFERENCES [Categories] ([Id]) 
); 

設計目標は、より高価な文字列としてログデータの大容量を可能にすることです別の表にまとめられています。

意味的に、両方のテーブルには、このビューで定義された単一の論理表を形成する:

CREATE VIEW [dbo].[HistoricLogEntries] 
    AS SELECT o.PointInTime, o.Noise, c.App, c.[Source], c.LogLevel, c.Logger 
    FROM Occurences o 
    JOIN Categories c ON o.CategoryId = c.Id; 

私は今、私の悩みを開始場所であるビューの代わりに-の-挿入トリガを定義したいのですが。

CREATE TRIGGER InsteadTrigger on [dbo].[HistoricLogEntries] 
INSTEAD OF INSERT 
AS 
BEGIN 
    INSERT INTO Categories 
    SELECT i.App, i.[Source], i.LogLevel, i.Logger 
    FROM INSERTED i; 

    INSERT INTO Occurences 
    SELECT i.PointInTime, i.Noise, c.Id AS CategoryId 
    FROM INSERTED i 
    JOIN Categories c ON i.App = c.App AND i.[Source] = c.[Source] AND i.LogLevel = c.LogLevel AND i.Logger = c.Logger; 
END 

最初のインサートは、タプルがデータベースにすでにあるかどうかをチェックされていないことを明らかに最初の問題があります:私は、次の試みを持っています。私は単一の値の挿入の場合にそれを行う方法を知っていたが、ここで私は複数行の挿入を考慮する必要があります。

私が説明できないもう一つのことは、最初の挿入が成功した場合にトリガーが機能しないことです。最初の挿入が実際に何も挿入していないかのように、私は外部キー違反を取得します。

私はこれが一般的な設定であるべきだと考えました。 SQL2008のために

答えて

8

+私は新しいカテゴリを挿入するためにMERGE文を使用しますとデータの挿入からの同時セッションを防ぐために

[...]:

MERGE dbo.Categories WITH (HOLDLOCK) AS c 
USING INSERTED AS i ON i.App = c.App 
AND i.[Source] = c.[Source] 
AND i.LogLevel = c.LogLevel 
AND i.Logger = c.Logger 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (App, [Source], LogLevel, Logger) 
    VALUES (i.App, i.[Source], i.LogLevel, i.Logger); 

私はHOLDLOCKテーブルヒントto prevent race conditionを使用同じ キーでは、1つのセッションを確実にするために互換性のないロックを取得する必要があります。 はキーを読み取ることができます(この場合、私の注記:UK_Categories一意インデックス)。ロックはトランザクションFKエラーが発生することを防止するの完了[...]

そして:。

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Category". The conflict occurred in database "Test", table "dbo.Categories", column 'Id'. 

私は、このように二INSERT内のすべての列の名前を追加します。

INSERT INTO Occurences (PointInTime, Noise, CategoryId) 
SELECT i.PointInTime, i.Noise, c.Id AS CategoryId 
FROM INSERTED i 
JOIN Categories c ON i.App = c.App 

理由これらのFKエラーのうち、列の順序の不一致:

1)CREATE内の列の順序表は、列の順序が異なる第2のインサート内

[PointInTime] BIGINT NOT NULL, 
    [CategoryId] INT NOT NULL, 
    [Noise]  INT NOT NULL, 

しかし

2)であり、()ノイズ対区分を参照してください。

ALTER TRIGGER InsteadTrigger on [dbo].[HistoricLogEntries] 
INSTEAD OF INSERT 
AS 
BEGIN  
    MERGE dbo.Categories WITH (HOLDLOCK) AS c 
    USING INSERTED AS i ON i.App = c.App 
    WHEN NOT MATCHED BY TARGET THEN 
     INSERT (App) 
     VALUES (i.App); 

    INSERT INTO Occurences (PointInTime, Noise, CategoryId) 
    SELECT i.PointInTime, i.Noise, c.Id AS CategoryId 
    FROM INSERTED i 
    JOIN Categories c ON i.App = c.App 
END 
GO 

INSERT INTO Occurences 
-- or INSERT INTO Occurences (PointInTime, CategoryId, Noise) 
SELECT i.PointInTime, i.Noise, c.Id AS CategoryId 
FROM ... 

これは私の解決策であります

+1

それは本当に包括的で、私は質問を投稿した後すぐにです。どうもありがとうございました! – John

関連する問題