【问题标题】:Alter Table Add Constraint vs Alter Table Add Check Constraint更改表添加约束与更改表添加检查约束
【发布时间】:2016-06-10 19:44:42
【问题描述】:

我在 SSMS 中遇到了一个奇怪的问题。

我的表设计大量使用复合键来在所有传递关系中强制执行严格和健壮的引用完整性 - 以违反 BCNF 为代价,但在实践中证明它非常方便,尤其是使用实体框架的自动导航属性(与我之前发布的这个问题:How can I enforce second-degree relationships without composite keys?)。

无论如何,我在使用 SSMS 2014 的图表编辑器添加外键关系时遇到问题。

这是我的问题的一个简化示例:

CREATE TABLE Tenants (
    TenantId bigint IDENTITY(1,1) PRIMARY KEY
)

CREATE TABLE Shops (
    TenantId bigint, -- Is a FOREIGN KEY( Tenants REFERENCES TenantId )
    ShopId   bigint IDENTITY(1,1)

    PRIMARY KEY( TenantId, ShopId )
)

CREATE TABLE Job (
    TenantId bigint, -- Is a FOREIGN KEY( Tenants REFERENCES TenantId )
    ShopId   bigint, -- Is a FOREIGN KEY( Shops REFERENCES TenantId )
    JobId    bigint IDENTITY(1,1)

    PRIMARY KEY( TenantId, ShopId, JobId )
)

我有许多包含这样列的表,并且使用图表编辑器创建具有复合键引用完整性的关系工作正常(您只需 ctrl+单击每个复合 FK 列,然后拖动到主键表,然后点击确定,就这样了。

但有时它会失败。例如,如果我在Jobs 中选择TenantIdShopId 并将它们拖到Shops 表上,它会给我这个错误:

表 'Shops' 中的列与现有的主键或 UNIQUE 约束不匹配。

...尽管这两列 Shops 表的主键!

我让 SSMS 为它试图添加的约束生成 SQL,它给了我这个(格式化我的,删除了额外的 TRANSACTION 代码):

ALTER TABLE
    dbo.Jobs
ADD CONSTRAINT
    FK_Jobs_Shops
FOREIGN KEY
    ( ShopId, TenantId ) REFERENCES dbo.Shops ( ShopId, TenantId )

ON UPDATE
    NO ACTION
ON DELETE
    NO ACTION 

当我直接运行它时,SQL Server 给了我这个错误:

消息 1776,第 16 级,状态 0,第 1 行 引用表 'dbo.Shops' 中没有与外键 'FK_Jobs_Shops' 中的引用列列表匹配的主键或候选键。

消息 1750,第 16 级,状态 0,第 1 行 无法创建约束或索引。查看以前的错误。

请注意,其他表已经与Shops 表定义了外键关系 - 所以我想知道发生了什么。所以我告诉 SSMS Script to CREATE 看似有效的约束 - 然后我重命名了一些东西以使其创建我最初想要的约束(在 JobsShops 之间),它给了我这个 不同输出(格式化我的):

ALTER TABLE
    [dbo].Jobs WITH CHECK
ADD CONSTRAINT
    [FK_Jobs_Shops]
FOREIGN KEY
    ([TenantId], [ShopId]) REFERENCES [dbo].[Shops] ([TenantId], [ShopId])
GO

ALTER TABLE
    [dbo].[Jobs]
CHECK CONSTRAINT
    [FK_Jobs_Shops]
GO

当我运行它时它起作用了!

注意区别:

  • 添加WITH CHECK
  • 缺少ON UPDATE/ON DELETE语句
  • 约束在两个语句中生效,而不是一个。

我的问题:

  • 我是否发现了 SQL Server 中的错误?
  • 两种约束定义语法之间是否存在语义差异?
  • 或者更确切地说,为什么一个有效而另一个无效?

【问题讨论】:

标签: sql-server


【解决方案1】:

两个脚本之间唯一显着的区别是外键字段/引用的主键字段的顺序。

失败的脚本使用了(ShopId, TenantId),这不是主键定义的顺序:

PRIMARY KEY(TenantId, ShopId) 

找不到官方参考,但 seems 必须按照与 PK 定义中相同的顺序指定 FK 字段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-06
    • 2015-02-19
    • 2015-05-06
    • 2017-01-07
    • 2014-10-01
    • 2020-04-26
    相关资源
    最近更新 更多