2017-07-21 27 views
1

我喜歡在3個表格之間創建'條件'(外鍵)關係。對我來說,是這樣的(當然,這是相當複雜,但我已經剝離下來來演示該問題的情況):添加與固定列值的關係

Table [ItemTable] 
Column int Id (PK) 
Column str ItemName 

Table [ItemGroup] 
Column int Id (PK) 
Column str GroupName 

Table [Settings] 
Column int Id (PK) 
Column str RefersTo ('I' means item, 'G' means item group) 
Column int Reference (foreign key depending on 'RefersTo') 

現在的目標是與這樣的約束上創建關係:

Settings.Reference refers to ItemTable.Id when Settings.RefersTo equals 'I' 
Settings.Reference refers to ItemGroup.Id when Settings.RefersTo equals 'G' 
No relation in case if RefersTo is empty (so no constraint in this situation) 

這聽起來像一個提及這裏或那裏的關係,但我不知道如何與MS SQL存檔。我通常在Management Studio中使用圖形設計器來創建和修改表格定義。

任何幫助表示讚賞。先謝謝你。

+1

爲什麼不用referenceI和referenceG列代替? – jarlh

+0

你當然不能這樣做 – Rahul

+0

這不是一個好的方法,可以使用兩個FKs,其中一個可能是已經建議的allways null,或者使用COMPOSITE OOP模式 - 這意味着該組可能是一種特殊類型的Item。 –

回答

0

角落找尋思前想後,我來到結論丟棄一列多關係啄的整體思路。接受

答:儘管在好還是壞主意,因爲所需的實現不可能的:)

謝謝大家對你的答案和評論!

0

這不是一個好的設計,如果可以的話,最好將它改爲@VojtěchDohnal已經建議的。

如果你不能改變它,你可以在插入後使用觸發器來檢查Reference的對應值是否來自正確的表,取決於RefersTo的當前值,如果不是,則停止插入並拋出一些錯誤,但使用觸發器也不是性能最好的方式。

你不能使用索引視圖(這將是最好的,因爲它會被模式綁定,它會從你的項目或組中獲得所有新值/刪除的值),因爲你的源是兩個不同的視圖,你會需要一個工會來生成一個可用值的完整列表,並且在索引視圖中存在一個限制The SELECT statement in the view definition must not contain UNION

最後一個選項:您可以使用一個附加表格,您可以在其中保留所有數據(Type('I', 'G'), Value (Id's from ItemTable for 'I', Id's from ItemGroup for 'G')),併爲每個表格添加可能的Id,然後使您的組合外鍵引用此新表格。

缺點是,在這種情況下,您需要跟蹤ItemTableItemGroup表中的更改,並相應地更新新創建的表(對於新插入的值或刪除的值),但它不太好維護。

對於這最後的場景代碼將是這樣的:

CREATE TABLE ItemTable (Id INT PRIMARY KEY IDENTITY(1,1), ItemName VARCHAR(100)) 
CREATE TABLE ItemGroup (Id INT PRIMARY KEY IDENTITY(1,1), GroupName VARCHAR(100)) 
CREATE TABLE Settings (Id INT PRIMARY KEY IDENTITY(1,1), RefersTo CHAR(1), Reference int) 

INSERT INTO ItemTable (ItemName) values ('TestItemName1'), ('TestItemName2'), ('TestItemName3'), ('TestItemName4') 
INSERT INTO [ItemGroup] (GroupName) values ('Group1'), ('Group2') 

SELECT * FROM ItemTable 
SELECT * FROM ItemGroup 
SELECT * FROM Settings 

CREATE TABLE ReferenceValues (Type char(1), Val INT, PRIMARY KEY (Type, Val)) 

INSERT INTO ReferenceValues 
SELECT 'I' as Type, i.Id as Val 
FROM dbo.ItemTable i 
UNION 
SELECT 'G' as Type, g.Id as Val 
FROM dbo.ItemGroup as g 

ALTER TABLE dbo.Settings 
ADD FOREIGN KEY (RefersTo, Reference) REFERENCES dbo.ReferenceValues(Type, Val); 

INSERT INTO Settings (RefersTo, Reference) 
VALUES ('I', 1) -- will work 

INSERT INTO Settings (RefersTo, Reference) 
VALUES ('G', 4) -- will not work 
1

外鍵沒有過濾器的條款在他們的定義。但是你可以使用計算列做到這一點:

create table Settings as (
    . . . 
    reference_i as (case when refersto = 'I' then reference end) persisted, 
    reference_g as (case when refersto = 'G' then reference end) persisted, 
    constraint fk_settings_reference_index 
     foreign key (reference_i) references itemTable(id), 
    constraint fk_settings_reference_group 
     foreign key (reference_g) references groupTable(id) 
); 
+0

這需要將reference_i和_g定義爲持久化,但除此之外,它完美地工作 - 超級解決方案! –