2014-01-31 16 views
0

我有一個表LotTablePK= LotID, Name, rateSQL Server:如何執行主鍵不能作爲主鍵在另一個表約束 - 觸發器

我有另一個表LotTranslate具有PK=TranslateLotIDFK=MasterLotID

插入之前到LotTable我需要確保強制插入PK是不是已經在LotTranslate的PK。

我的問題是我做觸發器而不是插入或刪除後?如果在LotTranslate找到PK,那麼最乾淨的方法是什麼?如何快速檢查其他表並停止插入LotTable

我的方向,我不知道這是否是正確的SQL Server的方式...

CREATE TRIGGER tr_LotsInsert ON LotTable 
INSTEAD OF INSERT 
AS 
BEGIN 
SET NOCOUNT ON 
    INSERT INTO dbo.LotTable 
    SELECT * 
    FROM INSERTED 
    WHERE INSERTED.LotID not in (select TranslateLotID from LotTranslate) 
END 

回答

1

我不推薦使用觸發器強制執行這一點。

你所描述的實際上是繼承,其中不同的對象共享一個基類型。在這種情況下,您的基本概念是Lot(稱爲超類型),以及兩個互斥,LotTableLotTranslate的子類型。 (爲了記錄,我認爲不幸的是,你的數據庫有一個名字爲Table的表格,除非它實際處理某種非數據庫對象的表格)。

有一個合理建立的數據庫設計模式來處理子類型和超類型:創建一個用作繼承模式中的「基礎對象」的父表,並使子類型表都具有FK關係它。要額外執行相互排他性,您可以將Type列添加到所有表中並將其包含在外鍵中。

然後,您的基表以1到0或1的關係參與兩個表。最重要的概念是LotID在所有表中始終相同,並且您不爲任何表創建單獨的代理鍵:基類/超類型表包含與子/子類型表中相同的值。

在我告訴你如何做到這一點之前,讓我提一下,在這種情況下,可能你的兩個表格應該被合併爲一個表格,並且有一個簡單的Type列表明哪一個當然會阻止單個Lot一次是兩種類型。然而,我假設你的兩個表有足夠多的列,它們之間會有很大的差別,這樣做會造成很大的浪費(如果只有幾列不同,最好將它們組合起來)。你可能想要做的工作,以獲得在子表的新LotTypeID

CREATE TABLE dbo.LotBase (
    LotID int NOT NULL CONSTRAINT PK_LotBase PRIMARY KEY CLUSTERED, 
    LotTypeID tinyint NOT NULL 
     CONSTRAINT FK_LotBase_LotTypeID FOREIGN KEY 
     REFERENCES dbo.LotType (LotTypeID), 
-- A unique constraint needed for FK purposes 
    CONSTRAINT UQ_LotBase_LotID_LotTypeID 
     UNIQUE (LotID, LotTypeID) 
); 

-- Include script here to create a LotType table and populate it with two rows 
-- 1 = `Standard Lot` and 2 = `TranslateLot` 

INSERT dbo.LotBase (LotID, LotTypeID) 
SELECT LotID, 1 
FROM dbo.LotTable; 

INSERT dbo.LotBase (LotID, LotTypeID) 
SELECT TranslateLotID, 2 
FROM dbo.LotTranslate; 

ALTER TABLE dbo.LotTable ADD LotTypeID tinyint NOT NULL 
    CONSTRAINT DF_LotTable_LotTypeID DEFAULT (1); 

ALTER TABLE dbo.LotTranslate ADD LotTypeID tinyint NOT NULL 
    CONSTRAINT DF_LotTranslate_LotTypeID DEFAULT (2); 

ALTER TABLE dbo.LotTable ADD CONSTRAINT FK_LotTable_LotBase 
    FOREIGN KEY (LotID, LotTypeID) 
    REFERENCES dbo.LotBase (LotID, LotTypeID); 

ALTER TABLE dbo.LotTable ADD CONSTRAINT FK_LotTable_LotBase 
    FOREIGN KEY (LotID, LotTypeID) 
    REFERENCES dbo.LotBase (LotID, LotTypeID); 

注立即向LotID列之後位於,但它是由你 - 只是要小心,因爲它需要表娛樂,如果你不熟悉和小心,你可能會傷害你的數據庫(先備份!)。

這種模式不會錯過的一個巨大好處是,在數據庫的任何地方你想要FK到Lot,你可以選擇使用其中一個子表或使用父表。這限制了你的其他表,以允許兩種或只有一種子類型。不要錯過的另一個好處是,您可以將兩個表格之間的公共列放入父表格中,而不是在兒童中重複。最後,您可以爲每個展示組合父+子列的子視圖創建一個視圖,就像原始子視圖一樣。

最後,如果您堅持繼續使用觸發器方法,則不必使用INSTEAD OF觸發器。你可以ROLLBACK這是不恰當的交易:

CREATE TRIGGER TR_LotTable_I ON dbo.LotTable FOR INSERT 
AS 
SET NOCOUNT ON; 
SET XACT_ABORT ON; 
IF EXISTS (
    SELECT * 
    FROM 
     Inserted I 
     INNER JOIN dbo.LotTranslate LT 
     ON I.LotID = LT.TranslateLotID 
) ROLLBACK TRAN; 

這是一個更好的方法來處理它(的一件事,你就不必每次都修改它,你一列添加到您的LotTable表。另外,我建議你學會使用(然後一貫地使用)JOIN語法,而不是你展示的IN語法。雖然我對這個建議有一些爭議,但根據我的經驗,使用IN而不是JOIN s錯過了一些重要的概念性學習,這些概念性學習繼續研究如何使它們變成JOIN s。還有其他實際益處,例如嵌套IN查詢會變得非常難以理解和維護,而添加5個以上的JOIN當格式良好時,並不會使查詢變得難以理解。

+0

謝謝埃裏克 - 我無法實現更大的解決方案這個表實際上沒有名稱中的TABLE,lotTranslate是一個很小的表格補丁來管理導致這個混亂的電話號碼的一些路由,所以你的觸發器工作像一個魅力。我瞭解IN,但加入對此很好,並且所有這些表格都進行了重新設計,因此這是一個臨時補丁,並且您的觸發器在幾個月內完美無缺,可以防止插入錯誤。 –