【问题标题】:Table with 1 of several FKs vs multiple cascade paths具有多个 FK 中的 1 个与多个级联路径的表
【发布时间】:2014-10-05 21:42:05
【问题描述】:

考虑以下 3 个表格:

create table FOO (
    foo_index Numeric(38, 0)
        primary key clustered
        constraint foo_foo_index_nn not null
        identity(1,1)
)

create table BAR (
    bar_index Numeric(38, 0)
        primary key clustered
        constraint bar_bar_index_nn not null
        identity(1,1),
    foo_index Numeric(38, 0)
        constraint bar_foo_index_nn not null
        constraint bar_foo_index_fk references FOO(foo_index)
        on delete cascade
)

create table BAZ (
    baz_index Numeric(38, 0)
        primary key clustered
        constraint baz_baz_index_nn not null
        identity(1,1),
    foo_index Numeric(38, 0)
        constraint baz_foo_index_fk references FOO(foo_index)
        on delete cascade,
    bar_index Numeric(38, 0)
        constraint baz_bar_index_fk references BAR(bar_index)
        on delete cascade
)

每个bar 都需要引用foo。虽然从表创建中看不出来,但其意图是让每个 baz 引用 foo bar,但绝不要同时引用两者。

如果您尝试创建上述架构,您将收到以下错误:
Introducing FOREIGN KEY constraint 'baz_bar_index_fk' on table 'BAZ' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

一旦 SQL Server 检测到多个级联路径(在这种情况下,baz 可能同时引用 barfoobar 引用和“foo”被删除),而不是尝试检测级联循环它只是不安全。

这在实际使用中永远不会发生,因为计划是只有两个外键之一是非空的。有什么方法可以向 SQL Server 指示使用限制,以便允许两个 FK 在级联时删除?

【问题讨论】:

  • 我最近遇到了同样的问题。我不认为这是可能的。您可以使用 DELETE 触发器或始终通过为您进行检查的存储过程进行删除来模拟这一点……后者并不总是可执行的。

标签: sql-server database-design foreign-keys cascade


【解决方案1】:

我认为你的设计有问题,因为它不是典型的外键约束 A Column Referenceing back to A TABLE。你不能在这里有外键约束,这是一个更复杂的业务需求,像外键约束这样简单的约束是不够的.....

我的建议

1) 将 NUMERIC(38,0) 更改为 INT,这些都是标识列,并且每次都会递增 1,我看不出将此列设置为 NUMERIC 38 的意义,这将是一个 17 字节的数据类型。 INT 将在此表中为您提供超过 20 亿行,bigint 将为您提供 2^63 行,更不用说 bigint 限制,如果您的表接近 20 亿行,您应该考虑对它进行分区而不是添加更大的数据类型来添加更多行,有这么大的桌子,你的表现会跌到谷底。

2) 正如您在表 BAZ 中提到的那样,一行只会引用 BAR 或 FOO 我看不出有两列的意义,然后添加一列以引用回任一表并添加另一列最小数据type (一个很好的候选 Bit 数据类型)来标识引用哪个表。

我会为你创建BAZ 类似这样的表.....

create table BAZ 
(
    baz_index INT primary key clustered
              constraint baz_baz_index_nn not null identity(1,1),
    bar_Fo_index INT,
    Is_Bar       BIT     --<--  1 is bar 0 is foo      
) 

级联操作

对于级联更新、删除或插入使用存储过程。

【讨论】:

  • 我想我会使用触发器来删除引用的foobar 级联到BAZ?
  • 我会使用存储过程,它给了你控制它的能力,触发器是另一种选择,但尽可能避免它们,它们是沉默的杀手:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多