带有常量的外键

Der*_*sar 15 foreign-key database-design sql-server constraint sql-server-2014

假设我有一个表 A,它有两列:一列是 的 ID ThingA,另一列是 的 ID ThingB。主键是(ThingA, ThingB).

接下来,我有第二个表,但这次它仅限于表A中具有ThingB = 3. 主键是ThingA,因为ThingB是 3 的常数。

最初,我以为我可以简单地:

FOREIGN KEY (ThingA, 3) REFERENCES A(ThingA, ThingB)
Run Code Online (Sandbox Code Playgroud)

但我了解到事实并非如此,我必须为以下内容创建一个列ThingB

ThingB INT NOT NULL DEFAULT(3) CHECK(ThingB = 3)
Run Code Online (Sandbox Code Playgroud)

然后,

FOREIGN KEY (ThingA, ThingB) REFERENCES A (ThingA, ThingB)
Run Code Online (Sandbox Code Playgroud)

有没有不需要额外列的替代方法,或者DEFAULT + CHECK?一种选择是持久化的计算列,但我也讨厌这个想法,因为它基本上是一种作弊,并且仍然添加了一个具有物理存储的新列。虽然它本身INT不会很大,但在几个表中有几百万行需要它,我宁愿不维护额外的列。

下面是示例 DDL 来说明这种情况:

CREATE TABLE Test1
(
    ThingA INT NOT NULL,
    ThingB INT NOT NULL,
    PRIMARY KEY (ThingA, ThingB)
);

CREATE TABLE Test2
(
    ThingAVal INT NOT NULL,
    ThingBVal INT NOT NULL DEFAULT(3) CHECK(ThingBVal = 3),
    Val INT NOT NULL,
    FOREIGN KEY (ThingAVal, ThingBVal) REFERENCES Test1 (ThingA, ThingB)
);
Run Code Online (Sandbox Code Playgroud)

我创建了一个 db<>fiddle 来演示我的(当前)解决方案:

如果答案是“否”,我会接受,但我很好奇是否还有其他选择。

pim*_*pim 0

dbo.Test1我认为代理键和在and语句之后执行的触发器的组合,特别是触发器,就是这里的答案。INSERTUPDATEINSTEAD OF

可能看起来像这样(小提琴):

模式

create table dbo.Test1 (
    Id int identity primary key,
    ThingA int not null,
    ThingB int not null
);

create table dbo.Test2 (
    Id int identity primary key,
    Test1Id int not null foreign key references Test1 (Id),
    Val int not null
);
Run Code Online (Sandbox Code Playgroud)

INSERT触发后

create trigger test2ThingBCheck_Insert 
on dbo.Test2  
instead of INSERT 
as  
begin

    insert into dbo.Test2 (Test1Id, Val)
    select
        t.Id
        ,i.Val
    from 
        Test1 t 
    join 
        inserted i 
        on i.Test1Id = t.Id
    where
        t.ThingB = 3;
end;
Run Code Online (Sandbox Code Playgroud)

UPDATE触发后

create trigger test2ThingBCheck_Update
on dbo.Test2  
instead of Update 
as  
begin
    update
        t2
    set
        Val = i.Val
    from
        dbo.Test2 t2
    join
        inserted i
        on i.Id = t2.Id        
    join
        dbo.Test1 t1
        on t1.Id = i.Test1Id
    where
        t1.ThingB = 3;
end; 
Run Code Online (Sandbox Code Playgroud)