【问题标题】:Add relation with fixed column value添加具有固定列值的关系
【发布时间】:2017-07-21 09:36:12
【问题描述】:

我喜欢在 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 中的图形设计器来创建和修改表格定义。

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

【问题讨论】:

  • 为什么不用referenceI 和referenceG 列呢?
  • 你当然不能这样做

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


【解决方案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)
);

【讨论】:

  • 这需要将 reference_i 和 _g 定义为持久化,但除此之外,它可以完美运行——多么好的解决方案!
【解决方案2】:

这不是一个好的设计,如果可以的话,最好按照@VojtěchDohnal 的建议进行更改。

如果无法更改,可以在插入后使用触发器,根据RefersTo的当前值,检查Reference对应的值是否来自正确的表,如果不是,停止插入并抛出一些错误,但使用触发器也不是性能方面的最佳方式。

您不能使用索引视图(这本来是最好的,因为它会受架构约束,并且会从您的项目或组中获取所有新值/已删除值),因为您的来源是两个不同的来源,您需要用于生成可能值的完整列表的联合,并且在索引视图中存在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

【讨论】:

    【解决方案3】:

    经过一番思考,我得出结论,用一列多关系的东西抛弃整个想法。

    已接受答案:无论想法好坏,都无法按预期实施 :)

    谢谢大家的回答和cmets!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      相关资源
      最近更新 更多