【问题标题】:SQL only allow foreign keys where other column matchesSQL 只允许其他列匹配的外键
【发布时间】:2025-12-27 12:50:11
【问题描述】:

我有两张桌子,

CREATE TABLE ActivityCodes (
    ActivityCodeID INT NOT NULL PRIMARY KEY,
    LocationID INT NOT NULL
);

CREATE TABLE LocationSettings (
    LocationID INT NOT NULL PRIMARY KEY,
    DefaultFooActivityCodeID INT,
    FOREIGN KEY (DefaultFooActivityCodeID) REFERENCES ActivityCodes(ActivityCodeID)
);

与所示的外键关系。活动代码仅对LocationSettings 表中给定的LocationIDDefaultFooActivityCodeID 有效,应为ActivityCodeID,其中ActivityCodes.LocationID == LocationSettings.LocationID。如何在 SQL 中强制执行该操作?可以用约束或外键来完成吗?有可能吗?


编辑:只是为了添加一些说明,这些表中的有效数据应该是这样的:

ActivityCodes

ActivityCodeID LocationID
1 123
2 123
3 456
4 456

LocationSettings

LocationID DefaultFooActivityCodeID
123 1
456 4

一个位置可以有多个活动代码。位置的默认活动代码必须是该位置的活动代码。 @Charlieface 我尝试按照answer you linked 中的建议使用复合外键,但我收到一条错误消息,指出ActivityCodes 上的LocationID 既不是唯一键也不是主键(我使用的是MS SQL Server)。

【问题讨论】:

标签: sql sql-server foreign-keys constraints


【解决方案1】:

您提供的 DDL 并不代表您所描述的内容。 DDL 对此进行了描述: ActivityCodes 独立于 LocationSettingsLocationSettings 依赖于 ActivityCodes(一个 ActivityCode 有多个 LocationSettings

外键定义良好,如FOREIGN KEY (DefaultFooActivityCodeID) REFERENCES ActivityCodes(ActivityCodeID)。如果您尝试在未先插入 ActivityCodes 的情况下插入 LocationSettings,则会因违反约束而失败。

【讨论】:

    【解决方案2】:

    外键引用不必是主键。这允许您对同一个表有两个外键引用,即使它们是多余的:

    CREATE TABLE ActivityCodes (
        ActivityCodeID INT NOT NULL PRIMARY KEY,
        LocationID INT NOT NULL,
        UNIQUE (LocationID, ActivityCodeID)
    );
    
    CREATE TABLE LocationSettings (
        LocationID INT NOT NULL PRIMARY KEY,
        DefaultFooActivityCodeID INT,
        FOREIGN KEY (DefaultFooActivityCodeID) REFERENCES ActivityCodes(ActivityCodeID),
        FOREIGN KEY (LocationID, DefaultFooActivityCodeID) REFERENCES ActivityCodes(LocationID, ActivityCodeID)
    );
    

    虽然这表达了你想要的,但你会发现在实践中维护起来有点棘手。设置默认值需要以下步骤:

    1. 插入具有NULL 默认值的位置。
    2. 使用默认活动在ActivityCodes 中插入一行。
    3. 更新LocationSettings中的默认值。

    【讨论】: