【问题标题】:Specify foreign key on one column and the value of another column在一列上指定外键和另一列的值
【发布时间】:2014-12-22 12:40:22
【问题描述】:

我有一个表ASSETS,其结构如下所示:

----------------------------------------------------
ID (PK) | DESCRIPTION | TYPE | Do- | Do+ | Dx- | Dx+
----------------------------------------------------

TYPE 列有一个外键,可能的值为SECURITYCURRENCY(即FX),我还有两个表:@987654327 @(例如EURRUBUSD):

--------------------------------------------------------
ID (PK)| FROM (FK ASSETS.ID) | TO (FK ASSETS.ID) | VALUE
--------------------------------------------------------

SECURITIES例如MTSGAZPVTB):

----------------------------------------------------------
ID (PK)(FK ASSETS.ID)| CURRENCY (PK)(FK ASSETS.ID) | VALUE
----------------------------------------------------------

如何进行约束,不仅在CURRENCIES.FROMCURRENCIES.TOSECURITIES.CURRENCY 中充当外键,而且还检查引用ASSETS.TYPE 是否为CURRENCY,并且在SECURITIES 中还检查如果为SECURITIES.ID 引用ASSETS.TYPESECURITY

我想我可以编写触发器来检查ASSETS.TYPE 的值,但我现在正在寻找另一种解决方案(如果可能的话,当然)。

如果有更好的方法来做想做的事情(作为更好的数据库设计),请分享你的想法。

附注我想这是一个很常见的问题,所以如果在这个网络上有关于它的文章或类似的问题或一些一般案例解决方案,请随时分享。

【问题讨论】:

  • 查看我的回答here。 (这个特定问题令人困惑地部分在其原始文本中没有学生表(您的 ASSESTS_DATA),但在其编辑图中有一个。)您的第一个设计就像我建议的那个(相对简单)(第一组项目符号),而您的可以按照我的建议进行修改/检查(第二组项目符号)
  • @philipxy :正如我已经标记了我的问题,FireBird。
  • 乍一看,将证券和货币仅视为两种资产对我来说似乎很奇怪。一个(安全)是具有价值的东西——无论是建筑物,仅仅是债务证明或其他任何东西——而另一个(货币)只是一个计量单位。我宁愿将证券和 Money 结合起来,Money 是不同货币的现金,不同银行账户中的钱等。
  • @Thorsten Kettner : FXSecurities 具有几乎相同的一组属性,如上图所示,在寻找投资组合等过程中成本或初始(+ 调整后的初始)和最低利润率,它们被视为资产,而不是金钱或证券。

标签: sql database database-design schema polymorphic-associations


【解决方案1】:

回答您最初的问题是使用额外的CHECK 约束,例如:

CREATE TABLE CURRENCIES (
   ...
   CONSTRAINT c_asset_from CHECK(exists(select 1 from ASSETS a where a.id = from and a.type = 'CURRENCY'))
);

TO 字段和SECURITIESCURRENCY 字段的类似约束。
但我认为你的新设计,对于securitycurrency 有单独的FK,是更好的设计。

【讨论】:

  • a.id = 来自?这意味着什么?当我尝试类似的事情时,我得到'在这种情况下不允许子查询。只允许标量表达式。因此没有从不同的表中选择。这对你有什么作用?
  • 不同的数据库引擎支持不同的特性。该问题最初被标记为FireBird,它支持这一点。我猜你使用的 mySql 没有
【解决方案2】:

从技术上讲,IMO 的设计可能会受到两类批评:

  • 在资产表中有一个名为 type (Polymorphic Association anti-pattern) 的两用外键。
    这将违反第一范式(原子 问题),失去参照完整性。
    一个解决方案可能是 通过继承简化关系。
    有一个基础 名为 Money 的货币和证券表的表,包含它们的共享属性,例如 name
    Money 表的主键将是 CurrencySecurity 表的主键。
    Asset 中包含Money 的外键将是解决方案。
  • 在资产表上使用surrogate identifier,这将导致 在架构设计中丢失业务逻辑。
    我更喜欢拥有 资产表中的复合主键PK{ID, TYPE(money fk)}.
    然后有 检查CURRENCIESSECURITIES 上的约束将解决 问题。
    CURRENCIES_chk {FK.CURRENCY = FK_TO.Money && FK.CURRENCY = FK_FROM.Money} SECURITIES_chk {FK.SECURITY = FK.Money}

【讨论】:

  • 有这样的想法:表ASSET_TYPE(一个字段ID,可能的值SECURITY/CURRENCY),表MONEY(这个表不是一个好的名称选择,但是为了与您的答案兼容,我将保持原样)(两个字段,IDTYPEFKASSET_TYPE.ID))。并在MONEY.TYPE 上插入SECURITIES 表或CURRENCIESFX,选择正确的词)中的触发器。我想我只能使用一个触发器来解决这个问题。在您的设计中,我只看到一个流程:我可以将 GAZP 之类的证券放入 CURRENCY 表中,反之亦然。
  • 对不起我的英语不好。在我的回答中没有任何触发机制,只存在两个检查约束!可能是我不了解您的设计领域。让我们一步一步地展示一个场景,看看会发生什么。首先我们将GAZP定义为Security中的一行?
  • 我不认为你没有理解这个概念,我只是不知道如何确保证券不会进入货币表(aka fx),反之亦然。此外,它们都应该在货币表中。
  • 在 SECURITIES 表中 SECURITY.FK = ASSET.TYPE 之间的 CHECK CONSTRAINT 不能保证这一点?
【解决方案3】:

您可以通过更改键的设计并使用识别关系来声明式地做到这一点。

这是蓝图:

看看ASSET.ASSET_TYPE是如何通过两个“分支”传播的,只是被合并到SECURITY.ASSET_TYPE中。

由于SECURITY.ASSET_TYPE 只是一个字段,一个SECURITY 行永远不能连接到多种资产类型。换一种说法:如果ASSETCURRENCY 连接到同一个SECURITY,那么它们必须具有相同的ASSET_TYPE

除此之外,CURRENCY 永远不能指向不同类型的ASSETs。

您可以根据需要将旧代理键(和其他字段)带回此模型。


话虽如此,生成ASSET_NO 会带来一些挑战。

  • 您可以只使用auto-incrementing 内置在您的DBMS 中的机制,但这会留下“漏洞”(即两种不同的资产类型永远不会使用相同的整数,即使它们在技术上可以使用)。
  • 或者您可以手动查找下一个值,但在这种情况下您必须处理并发(通过锁定序列化插入,或者在并发事务尝试相同值的情况下重试插入)。

【讨论】:

    【解决方案4】:

    您可以为此使用检查。 你想硬编码这些值吗?

    CREATE TABLE Persons
    (
        P_Id int NOT NULL,
        LastName varchar(255) NOT NULL,
        FirstName varchar(255),
        Address varchar(255),
        City varchar(255),
        CONSTRAINT chk_Person CHECK (P_Id>0 AND City='Sandnes')
    )
    

    来源:W3schools

    使用 firebird 可能需要不同的语法。 看一看:Firebird reference

    【讨论】:

    • Firebird CHECK 的语法是一样的。
    • 在您的示例中,您检查了同一个表的列,但您能否提供一个指向另一个表中列的值的示例?
    • @notulysses 在这种情况下,您最好重新考虑模型或使用触发器。
    猜你喜欢
    • 2019-12-04
    • 1970-01-01
    • 2020-04-12
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 2015-09-04
    相关资源
    最近更新 更多