添加与固定列值的关系

时间:2017-07-21 09:36:12

标签: sql sql-server database foreign-keys relationship

我喜欢在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中的grafical designer来创建和修改表格定义。

感谢任何帮助。提前谢谢。

3 个答案:

答案 0 :(得分: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)
);

答案 1 :(得分: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

答案 2 :(得分:0)

在思考完毕后,我得出结论,用一栏多关系来抛弃整个想法。

接受答案:尽管有好的或坏的想法,但仍然无法实施:)

谢谢大家的回答和评论!