当前位置: 首页 > 面试题库 >

将外键关系限制为相关子类型的行

戎鹏云
2023-03-14
问题内容

概述:我试图表示数据库中几种类型的实体,它们具有许多共同的基本字段,然后每个实体都具有一些其他字段,这些字段不与其他类型的实体共享。工作流通常会涉及将实体一起列出,因此我决定要有一个带有其公共字段的表,然后每个实体都将具有自己的表及其附加字段。

要实现:所有实体都拥有一个共同的领域“
tatus”;但是,某些实体将仅支持所有可能状态的子集。我还希望每种类型的实体都强制使用其状态子集。最后,我还将希望在将实体一起列出时将其包括在内,因此将其从一组通用字段中排除似乎是不正确的,因为这将需要特定类型表的并集,并且在其中缺少``实现接口’‘。
SQL意味着包含该字段将是约定俗成的。

我为什么在这里:以下是可以使用的解决方案,但是如果有更好或更通用的方法来解决问题,我很感兴趣。特别是,这种解决方案需要我做出多余的unique约束,而多余的状态字段却让人感到不舒服。

create schema test;

create table test.statuses(
    id      integer     primary key
);
create table test.entities(
    id      integer     primary key,
    status  integer,
    unique(id, status),
    foreign key (status) references test.statuses(id)
);

create table test.statuses_subset1(
    id      integer     primary key,
    foreign key (id) references test.statuses(id)
);
create table test.entites_subtype(
    id integer primary key,
    status integer,
    foreign key (id) references test.entities(id),
    foreign key (status) references test.statuses_subset1(id),
    foreign key (id, status) references test.entities(id, status) initially deferred
);

一些数据:

insert into test.statuses(id) values
    (1),
    (2),
    (3);
insert into test.entities(id, status) values
    (11, 1),
    (13, 3);
insert into test.statuses_subset1(id) values
    (1), (2);
insert into test.entites_subtype(id, status) values
    (11, 1);

-- Test updating subtype first
update test.entites_subtype
    set status = 2
    where id = 11;
update test.entities
    set status = 2
    where id = 11;

-- Test updating base type first
update test.entities
    set status = 1
    where id = 11;
update test.entites_subtype
    set status = 1
    where id = 11;

/* -- This will fail
insert into test.entites_subtype(id, status) values
    (12, 3);
*/

问题答案:

简化基于[`MATCH

SIMPLE`](http://www.postgresql.org/docs/current/interactive/sql-
createtable.html)fk约束的行为

如果至少一列具有默认MATCH SIMPLE行为的多列外部约束为NULL,则不强制执行约束。您可以在此基础上大大简化您的设计

CREATE SCHEMA test;

CREATE TABLE test.status(
   status_id  integer PRIMARY KEY
  ,sub        bool NOT NULL DEFAULT FALSE  -- TRUE .. *can* be sub-status
  ,UNIQUE (sub, status_id)
);

CREATE TABLE test.entity(
   entity_id  integer PRIMARY KEY
  ,status_id  integer REFERENCES test.status  -- can reference all statuses
  ,sub        bool      -- see examples below
  ,additional_col1 text -- should be NULL for main entities
  ,additional_col2 text -- should be NULL for main entities
  ,FOREIGN KEY (sub, status_id) REFERENCES test.status(sub, status_id)
     MATCH SIMPLE ON UPDATE CASCADE  -- optionally enforce sub-status
);

这是 很便宜 存放一些额外的NULL列(主要实体):

  • 使用postgresql DB需要多少磁盘空间来存储NULL值?

顺便说一句,根据文档:

如果 refcolumn 省略列表, reftable 则使用的主键。

演示数据:

INSERT INTO test.status VALUES
  (1, TRUE)
, (2, TRUE)
, (3, FALSE);     -- not valid for sub-entities

INSERT INTO test.entity(entity_id, status_id, sub) VALUES
  (11, 1, TRUE)   -- sub-entity (can be main, UPDATES to status.sub cascaded)
, (13, 3, FALSE)  -- entity  (cannot be sub,  UPDATES to status.sub cascaded)
, (14, 2, NULL)   -- entity  (can    be sub,  UPDATES to status.sub NOT cascaded)
, (15, 3, NULL)   -- entity  (cannot be sub,  UPDATES to status.sub NOT cascaded)

SQL Fiddle (包括您的测试)。

单个FK的替代方案

另一个选择是将的所有组合输入(status_id, sub)status表中(每个只能有2个status_id),并且只有一个fk约束:

CREATE TABLE test.status(
   status_id  integer
  ,sub        bool DEFAULT FALSE
  ,PRIMARY KEY (status_id, sub)
);

CREATE TABLE test.entity(
   entity_id  integer PRIMARY KEY
  ,status_id  integer NOT NULL  -- cannot be NULL in this case
  ,sub        bool NOT NULL     -- cannot be NULL in this case
  ,additional_col1 text
  ,additional_col2 text
  ,FOREIGN KEY (status_id, sub) REFERENCES test.status
     MATCH SIMPLE ON UPDATE CASCADE  -- optionally enforce sub-status
);

INSERT INTO test.status VALUES
  (1, TRUE)       -- can be sub ...
  (1, FALSE)      -- ... and main
, (2, TRUE)
, (2, FALSE)
, (3, FALSE);     -- only main

等等。

相关答案:

  • 完全匹配与简单匹配
  • 仅当第三列不是NULL时,两列外键约束
  • 当验证在另一个表上有条件时,数据库中的唯一性验证

保留所有桌子

如果出于某种原因不需要所有四个表,请考虑对dba.SE上一个非常类似的问题进行详细的解决:

  • 强制约束“远离桌子”。

遗产

…可能是您描述的另一种选择。如果您可以忍受一些主要限制。相关答案:

  • 在PostgreSQL中创建两种类型的表


 类似资料:
  • 问题内容: 我有一种类似于以下的双向外交关系 我如何将Parent.favoritechild的选择限制为仅其父母为自己的孩子?我试过了 但这会导致管理界面未列出任何子项。 问题答案: 我刚刚在Django文档中碰到。尚不确定这是如何工作的,但在这里可能是正确的事情。 允许指定常量,可调用对象或Q对象以限制键的允许选择。常量在这里显然没有用,因为它对所涉及的对象一无所知。 使用可调用(函数或类方法

  • 问题内容: 我如何在MySQL中应用distinct关键字,以便仅检查一个列字段是否唯一,同时仍从表中返回其他列? 问题答案: 为了能够做到这一点,mysql必须知道如何处理其他列。您GROUP BY列应该是唯一的,并使用一个函数来告诉它如何处理其他列(所谓的 汇总函数 )。和是常见的例子:

  • 本文向大家介绍关系类型,包括了关系类型的使用技巧和注意事项,需要的朋友参考一下 X和Y或E上的集合之间的空关系是空集合∅ 集合X和Y之间的完全关系为集合X×Y 集合X上的身份关系是集合{(x,x)| x∈X} 关系R的逆关系R'定义为-R'= {(b,a)| (a,b)∈R} 示例-如果R = {(1,2),(2,3)},则R'将是{(2,1),(3,2)} 如果∀a∈A与a相关(aRa成立),则

  • 问题内容: 我有一个用户表和一个朋友表。朋友表拥有两个外键,分别指向我的用户表和状态字段。我试图能够从Friend对象上的User表中调用属性。例如,我希望能够执行诸如friend.name或friend.email之类的操作。 当我得到对象时,我所拥有的只是2,并且我想显示每个用户的所有属性,以便可以在表单等中使用该信息。我对sqlalchemy还是陌生的- 仍在尝试学习更高级的功能。这只是一个

  • 我有一个奇怪的问题。 我有一个用户表和一个公司表。一个用户属于一家公司,而一家公司有许多用户。 表的两个主键都是id。 在laravel文档中,我阅读了以下内容: 此外,Eloquent假设外键的值应该与父项的id列匹配。 我在我的

  • null null 表名: ISBN->外键 authorid->外键 既然Linq-to-SQL会根据外键关系自动创建属性,为什么下面的代码块不会触发智能感知呢?对我来说不是... 它不允许我添加照片,但“author”与AuthorISBN有“一对多”关系,“titles”也与AuthorISBN有“一对多”关系 代码: