【问题标题】:Composite FK with nullable fields具有可为空字段的复合外键
【发布时间】:2018-03-23 02:39:19
【问题描述】:

我有 3 张桌子:

  • NETWORK_OPERATORs;

  • NETWORK_CELLs:每个人都属于一个NETWORK_OPERATOR

  • IRIs:他们每个人都可以有任何一个

    1. 网络运营商
    2. 一个网络单元

但 1) 和 2) 之一是强制性的。

在 1) 的情况下,netOpId 必须存在于 NETWORK_OPERATOR 表中;

在 2) 的情况下,cellId+netOpId 必须存在于 CELL 表中;

这是一个示例 DDL 代码:

CREATE TABLE "NETWORK_OPERATOR" (
  "NETOPID" INTEGER NOT NULL, 
  "NAME" VARCHAR2(20),
  CONSTRAINT "NETWORK_OPERATOR_PK" PRIMARY KEY ("NETOPID")
)

CREATE TABLE "NETWORK_CELL" (
  "CELLID" INTEGER  NOT NULL, 
  "NETOPID" INTEGER  NOT NULL, 
  "NAME" VARCHAR2(20), 
  CONSTRAINT "NETWORK_CELL_PK" PRIMARY KEY ("CELLID"),
  CONSTRAINT "CELL_NETOPS_FK" FOREIGN KEY ("NETOPID") REFERENCES "NETWORK_OPERATOR" ("NETOPID")
)

CREATE TABLE "IRI" (
  "IRIID" INTEGER NOT NULL,
  "NETOPID" INTEGER,
  "CELLID" INTEGER,
  "NAME" VARCHAR2(20),
  CONSTRAINT "IRI_PK" PRIMARY KEY ("IRIID"),
  CONSTRAINT "IRI_NETOPS_FK" FOREIGN KEY ("NETOPID") REFERENCES "NETWORK_OPERATOR" ("NETOPID")
)

换句话说

NETWORK_CELL 本身总是绑定到 NETWORK_OPERATOR,因此 IF IRI 有一个 netOpId 它应该被强制为现有的netOpIdELSE IF 一个IRI 有一个cellId+netOpId 它应该被强制为一个现有的cellId+netOpId

我看到了 2 个选项:

选项 1:

只制作IRI.NETOPID NOT NULLable 并添加复合外键

    CREATE TABLE "IRI" (
      ...
      "NETOPID" INTEGER NOT NULL,
      "CELLID" INTEGER,
      ...
      CONSTRAINT "IRI_CELL_FK" FOREIGN KEY ("CELLID", "NETOPID") REFERENCES "NETWORK_CELL" ("CELLID", "NETOPID")

)

(当然"NETWORK_CELL" ("CELLID", "NETOPID")上会有唯一键)

换句话说,IRI 将与网络运营商具有强制性的 FK 关系,与网络单元具有可选的 FK 关系。

“可疑”的事情是这个“可选”FK 是由 IRI 方面的一个必填字段和一个可选字段组成的。

Oracle RDBMS 接受这一点(我刚刚尝试过),但这是一个好习惯吗?

选项 2:

与选项 1 中相同的 FK,但保留 IRI.NETOPID 可为空并添加一个自定义约束以强制执行 either netOpId or netOpId+cellId

我觉得这个解决方案更便携,但也许我错了。

问题

还有更好的选择吗?

处理这种情况的最佳做法是什么?为什么? 我也在考虑移植到其他 RDBMS...

谢谢

【问题讨论】:

  • 嗨。这是一个常见问题解答。谷歌 sql/数据库子类型/继承/多态。还有许多/多个 FK 到许多/多个表(反模式)。你应该找到这些谷歌搜索——总是用谷歌搜索你的问题/问题/目标的许多清晰、简洁和具体的版本/措辞,并阅读许多答案。将您发现的相关关键字添加到搜索中。如果您没有找到答案,请发布,使用一个变体搜索您的标题和关键字作为您的标签。 PS 几乎总是最简单和最直接的设计是从没有 NULL 开始的。您始终可以通过左连接到新的基表来使用 NULL。
  • @philipxy 我的问题与继承无关。我仔细 google 寻​​找关于这种关系的最佳实践,但找不到答案。顺便说一句,谢谢你的建议。我会尽量解释清楚。
  • 请给minimal reproducible example。您的 if-elseif 没有意义,因为如果 IRI 没有 netOpId 则它没有 cellId+netOpId 所以 elseif 永远不会满足。 “有”和“+”似乎隐藏了一些语言。例如,您在评论中说“如果它只是 有一个操作...”。同样,“总是绑定到”也不清楚,它只是您立即尝试给出结果的模糊事物。 (我猜这应该是一个定义?但你开始“so”而不是“ie”。) PS FK 默认 MATCH SIMPLE 的工作方式,满足具有 NULL 的 FK。所以你可以有 FKs (netid), (cellid) & (netid, cellid)。
  • 子类型是这里的一个问题。 IRI 可以“拥有”事物的 type 事物有两种 typesHere's another link。我同意这可能不是这里唯一的设计问题。

标签: oracle foreign-keys rdbms


【解决方案1】:

您的选项 1 没问题。默认 FK(外键)声明模式 MATCH SIMPLE(通常是唯一实现的)的工作方式,具有任何 NULL 的 FK 子行值满足其约束。所以你可以有 IRI FKs (netid) & (netid, cellid)——加上 netid NOT NULL。 (您似乎忘记了第一个 IRI 中的 NOT NULL,但不是第二个。)

那么列对的唯一情况是 (non-null, null) & (non-null, non-null)。必须存在netid;一个非空的 cellid 必须与该 netid 存在并且一个空的 cellid 是可以的。

【讨论】:

    【解决方案2】:

    据我了解,一种解决方案可能是这个:

    CREATE TABLE IRI (
      IRIID INTEGER NOT NULL,
      NETOPID INTEGER,
      CELLID INTEGER,
      NAME VARCHAR2(20),
      CONSTRAINT IRI_PK PRIMARY KEY (IRIID),
      CONSTRAINT IRI_NETOPS_FK FOREIGN KEY (NETOPID) REFERENCES NETWORK_OPERATOR (NETOPID),
      CONSTRAINT IRI_CELLS_FK FOREIGN KEY (CELLID) REFERENCES NETWORK_CELL (CELLID),
      CONSTRAINT IRI_CELL_OR_NETOP CHECK ( NVL(NETOPID, CELLID) IS NOT NULL )
    )
    

    如果你想强制只设置值,你可以使用

    CHECK ( NVL(NETOPID, CELLID) IS NOT NULL AND NOT (NETOPID IS NOT NULL AND CELLID IS NOT NULL) )
    

    CHECK ( NVL(NETOPID, CELLID) IS NOT NULL AND NETOPID||CELLID IN (NETOPID, CELLID) )
    

    CHECK ( (NETOPID IS NULL AND CELLID IS NOT NULL) OR (NETOPID IS NOT NULL AND CELLID IS NULL) )
    

    【讨论】:

    • 谢谢,但是这个话题的关键是NetworkCell本身绑定了一个运算符,所以如果IRI只有一个运算符,它应该绑定到NETWORK_OPERATOR on仅限NETOPID,但如果IRI 有一个单元格,它应该绑定到NETOPID + CELLID 上的NETWORK_CELL。我会尽量让问题更清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 2011-09-06
    • 1970-01-01
    • 2019-06-23
    • 2018-02-02
    相关资源
    最近更新 更多