2013-10-19 23 views
5

對於測井系統的我想有以下數據庫模式:INSTEAD-OF-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 

有一個明顯的第一個問題,第一個插入不檢查元組是否已經在數據庫中。我知道如何在單個值插入的情況下做到這一點,但在這裏我必須考慮多行插入。

另一件我不能解釋的事情是,如果第一次插入成功,觸發器甚至不起作用。我得到一個外鍵違例 - 就好像第一個插入沒有實際插入任何東西。

我認爲這應該是一個常見的設置,所以也許有人有一些類似的示例代碼?

回答

8

對於SQL2008 +我會用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

[...]爲了防止併發會話從與插入數據相同的 鍵,必須獲取不兼容的鎖以確保只有一個會話 可以讀取密鑰(本例中爲我的註釋: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中的列順序表是

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

2)在第二插入列的順序是不同的(見的CategoryId VS噪音):

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

這是我的解決方案:

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 
+1

這真的很全面,在我發佈這個問題後很快。非常感謝你! – John