在一列上指定外键和另一列的值

时间:2014-12-22 12:40:23

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

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

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

TYPE列有外键,可能的值为SECURITYCURRENCY(即 FX ),我也有另外两个表:CURRENCIES例如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是否{{1在} CURRENCY中还会检查SECURITIES ASSETS.TYPE的{​​{1}}是否为SECURITIES.ID

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

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

P.S。我想这是一个非常普遍的问题,所以如果有关于它的文章或者在这个网络上提出的类似问题或者一些通用案例解决方案,请随时分享。

4 个答案:

答案 0 :(得分:4)

对原始问题的回答是使用额外的CHECK 约束,如:

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

TO字段以及SECURITIES CURRENCY字段的类似约束。
但我认为你的新设计,securitycurrency的单独的 FK ,是更好的设计。

答案 1 :(得分:2)

IMO从技术上讲,设计可能会分为两类:

  • 在资产表中有一个名为 type Polymorphic Association anti-pattern)的两用外键。
    这将违反第一范式(原子 问题),失去参照完整性。
    解决方案可以 通过继承简化关系。
    有基础 名为Money的货币和安全表的表,包含它们的共享属性,如name
    Money表的主键将是CurrencySecurity表的主键 将Money内的Asset外键设为解决方案。
  • 在资产表上使用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}

    enter image description here

答案 2 :(得分:1)

你可以使用支票。 你想硬编码这些值吗?

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

答案 3 :(得分:1)

您可以通过更改密钥的设计和使用识别关系来声明性地执行此操作。

以下是蓝图:

enter image description here

了解ASSET.ASSET_TYPE如何通过"分支"传播,仅在SECURITY.ASSET_TYPE合并。

由于SECURITY.ASSET_TYPE只是一个字段,因此一个SECURITY行永远不能连接到多个资产类型。换句话说:如果ASSETCURRENCY与同一SECURITY相关联,则它们必须具有相同的ASSET_TYPE

除此之外,CURRENCY永远不会指向不同类型的ASSET

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


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

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