【问题标题】:SQL table referencing multiple foreign keys, while defining order and guaranteeing referential integritySQL 表引用多个外键,同时定义顺序并保证引用完整性
【发布时间】:2011-06-01 12:22:30
【问题描述】:

我的客户已指定,当满足内部逻辑中的某些条件时,他需要执行不同的操作。每种操作类型(到目前为止CommandWriteVariable)都有一组单独的特定信息,因此需要存储在单独的表中。用户必须能够定义执行操作的顺序。

我有以下数据库设置:

LogicTable
* OutputID
* Description

OutputTable <== a pure relational table
* OutputID
* LogicID
* ActionID <== this references one of the action tables (Command/WriteVariable)
* ActionTypeID
* Sequence

ActionTypeTable
* ActionTypeID
* Description

CommandTable
* CommandID  <== corresponds to ActionID in OutputTable
three columns with further command-specific information

WriteVariableTable
* WriteVariableID  <== corresponds to ActionID in OutputTable
four columns with further write-variable-specific information

我的问题是我不能有多个关系表,因为我不能保证跨多个表的操作顺序。对于输出表中的每个单独操作,我不能有多个带有外键的列(客户要求)。使用上述设置,我无法具有引用完整性,由于外键条目没有对应的主键条目,因此在我的应用程序中可能会导致 ConfigurationException

是否有一种设计可以实现引用完整性并设法保证引用操作的顺序?

【问题讨论】:

  • 您能否扩展(客户需求)部分——具体来说,该领​​域的需求是什么?是不是没有对 OutputTable 定义进行任何更改?或者不存储或“冗余”数据,或者必须始终设置 ActionID,还是什么?
  • @Damien_The_Unbeliever:客户要求指定我不能在键列中有空值。这意味着我不能指定多个列,每个操作类型一个,并使用它来引用基础操作。

标签: c# sql database-design foreign-keys


【解决方案1】:

如果客户允许,您可以将计算列添加到 OutputTable 表中,例如

create table OutputTable (
  OutputID <datatype> <nullability>,
 LogicID <datatype> <nullability>,
 ActionID <datatype> <nullability>,
 ActionTypeID <datatype> <nullability>,
 Sequence <datatype> <nullability>,
  CommandActionID as CASE WHEN ActionTypeID = <Command Action> then ActionID END PERSISTED,
  WVActionID as CASE WHEN ActionTypeID = <Write Variable Action> then ActionID END PERSISTED,
    constraint FK_Output_CommandActions FOREIGN KEY (CommandActionID) references CommandTable (CommandID)
)

然后,您可以将这些计算列用作 FK 引用的来源。不过,我仍然发现来自客户的这种约束有点令人困惑 - 当然,您应该能够定义架构,以便其中包含的数据显然是正确的 - 其他任何事情都会在未来引发完整性问题。

【讨论】:

  • 如果ActionID 在每个子表中被引用并在Output.Table 中定义,这将起作用。然而反过来:OutputTable.ActionID 本身就是对CommandTable.CommandIDWriteVariable.WriteVariableID 的主键之一的引用。
  • @froeschili - 我添加了一个 FK 约束。我相信这是正确的方法。
  • 当我尝试使用此类计算字段创建表并使用其中一个作为外键引用时,我收到此错误消息Computed Column 'CommandActionID' in table 'IA_OutputTable' is invalid for use in 'FOREIGN KEY CONSTRAINT' because it is not persisted.
  • @froeschili - 在那里很容易修复(抱歉,我自己没有表格,所以我无法测试我的答案)。在计算列定义的末尾添加单词persisted(在END, 之间)
  • 感谢您的快速回复。添加persisted 确实有效。如果这仍然符合他的要求,我将不得不与客户核对。我认为拥有null 值应该没有问题,因为这些列是计算出来的,用户不能错误配置。我还必须在星期一与我的同事讨论这个问题。
【解决方案2】:
  • 每个动作都是简单类型(意味着没有子动作)。
  • "..when certain conditions in an internal logic are met" 被称为商业活动
  • 每个业务事件都属于特定的事件类型
  • 特定事件类型的每个业务事件都会导致一组操作
  • 动作序列号指定每个动作集动作的顺序。

【讨论】:

  • 几乎每个 BusinessEvent 都有一个唯一的 ActionSet,因此 EventType 几乎是不必要的。不过,我会把它记在脑后。我认为关键是将我的OutputTable 拆分为一个Output 和一个与Action/ActionSet 类似的OutputSet。我将不得不在星期一与我的同事讨论这个问题。我认为这可能会让你得到一个公认的答案。
【解决方案3】:

两种可能:

  1. 能否不将 OrderingId 列添加到用于确定要执行的操作顺序的 Command 和 WriteVariable 表中?您需要以编程方式确保使用的 orderingIds 正确增加,因为数据库无法确保跨表的唯一性,因此请在应用程序代码或触发器/存储过程中执行此操作

  2. 有一个带有 orderingID 的 Action 表(这可能是您的 OutputTable 表)

【讨论】:

  • To 1. 无法通过应用程序代码检查正确的增加,因为客户希望能够手动更改数据库中的值。我对 tirggers / 存储过程不够熟悉,无法确保自己。到 2. 我已经在Output.Table 中有一个序列(为了我的缘故将其称为 OrderID)。这并不能解决参照完整性问题。
  • 听起来引用完整性是倒退的,即 CommandTable 和 WriteVariableTable 都应该有一个引用 OutputTable PK 的 FK(实际上它们的 PK 也应该是 OutputTable 的 FK)而不是 OutputTable 引用其中任何一个另外两个表。
  • 我怎样才能定义多个引用相同操作的逻辑/输出,即相同的命令?
【解决方案4】:

我真的没有看到问题....您已经有了定义序列的OutputTable,对吗?输出表上还有ActionID

所以现在,您的每个“子”表(如 CommandTableWriteVariableTable)都应该只引用 OutputTable.ActionID。在 SQL Server 中,要做到这一点,你需要在ActionID 上加上一个UNIQUE INDEX,然后你可以定义:

ALTER TABLE dbo.CommandTable
   ADD CONSTRAINT FK_CommandTable_OutputTable
   FOREIGN KEY(CommandID) REFERENCES dbo.CommandTable.ActionID

ALTER TABLE dbo.WriteVariableTable
   ADD CONSTRAINT FK_WriteVariableTable_OutputTable
   FOREIGN KEY(WriteVariableID) REFERENCES dbo.CommandTable.ActionID

现在您已经完全检查了引用完整性 - 您在 OutputTable 上定义了您的序列,您可以扩展它以包含其他操作类型的附加“子”表......

【讨论】:

  • OutputTable.ActionID 实际上是对子表的引用,而不是唯一的 ID。问题是,相同的动作可以由几个不同的逻辑元素执行。如果我要颠倒这个定义,我将有多个条目定义相同的操作,但引用不同的OutputTable.ActionID
【解决方案5】:

可能有无数种方法可以做到这一点。以我的经验,为每种类型的操作设置一个单独的表会增加复杂性并使其难以添加新操作。您可能需要考虑以下内容:

LogicTable
* OutputID
* Description

OutputTable <== a pure relational table
* OutputID
* LogicID
* ActionID
* ActionTypeID
* Sequence

ActionTypeTable
* ActionTypeID
* Description

Actions
* ActionID - primary key
* Sequence - not null
-- other columns as necessary to support the various
   actions, e.g. Command and WriteVariable

如果 Actions 中的某些列不用于每种不同类型的操作,那很好 - 一些空字段不会有任何影响。或者,您可以采用更通用的设计,例如

Actions
* ActionID - primary key
* Sequence - not null


ActionArguments
* ActionID - foreign key to Actions, part of primary key
* ArgumentName - not NULL, part of primary key
* ArgumentValue

只是几个想法。

分享和享受。

【讨论】:

  • 您的第二种选择看起来很有希望。但是,我如何检查每种操作类型是否具有正确数量的参数?如指定的那样,每种操作类型都有不同的签名并需要不同的附加信息。
  • 我想我可能会把它交给程序逻辑。尽管您可以在表触发器中执行类似的操作。
【解决方案6】:

简而言之,是的,我相信您的外键倒置了。这是我很快想到的:

【讨论】:

  • 您的解决方案与 Damir Sudarevic 的解决方案有何不同?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多