【问题标题】:Polymorphism in SQL database tables?SQL数据库表中的多态性?
【发布时间】:2010-10-08 09:00:45
【问题描述】:

我的数据库中目前有多个表,它们由相同的“基本字段”组成,例如:

name character varying(100),
description text,
url character varying(255)

但是我对该基本表有多个专业化,例如tv_series 具有字段seasonepisodeairing,而movies 表具有release_datebudget等等

现在起初这不是问题,但我想创建第二个表,称为linkgroups,并带有这些专用表的外键。这意味着我必须以某种方式对其内部进行规范化。

我听说过解决这个问题的一种方法是使用key-value-pair-table 对其进行规范化,但我不喜欢这个想法,因为它是一种“数据库内的数据库”方案,我没有办法要求某些键/字段,也不需要特殊类型,以后获取和排序数据会很痛苦。

所以我现在正在寻找一种在多个表之间“共享”主键甚至更好的方法:一种通过拥有一个通用表和多个专用表来对其进行规范化的方法。

【问题讨论】:

  • 好吧,我不能 100% 确定我没看错。你有一堆不同的表,它们有一些共同的结构。您想添加一个引用这些表的表,但不能使用 FK,因为每个表都有自己的 PK。如果可以的话,我会创建一个通用的主表并暂停它。
  • 是的,但我的问题是如何最优雅地做到这一点
  • 你是什么意思,“规范化”?将表重新排列为多个子类型表(可能加上一个公共/超类型表)或键值编码不是 DB 规范化。对更高 NF 的规范化将一个表替换为自然连接回该表的其他表。 PS 将单词放在吓人的引号中并不能阐明您没有通过实际说出您的意思来阐明的特殊含义。

标签: sql postgresql database-normalization


【解决方案1】:

是的,问题是您只希望一种子类型的一个对象引用父类的任何给定行。从@Jay S 给出的example 开始,试试这个:

create table media_types (
  media_type     int primary key,
  media_name     varchar(20)
);
insert into media_types (media_type, media_name) values
  (2, 'TV series'),
  (3, 'movie');

create table media (
  media_id       int not null,
  media_type     not null,
  name           varchar(100),
  description    text,
  url            varchar(255),
  primary key (media_id),
  unique key (media_id, media_type),
  foreign key (media_type) 
    references media_types (media_type)
);

create table tv_series (
  media_id       int primary key,
  media_type     int check (media_type = 2),
  season         int,
  episode        int,
  airing         date,
  foreign key (media_id, media_type) 
    references media (media_id, media_type)
);

create table movies (
  media_id       int primary key,
  media_type     int check (media_type = 3),
  release_date   date,
  budget         numeric(9,2),
  foreign key (media_id, media_type) 
    references media (media_id, media_type)
);

这是@mike g 的不相交子类型mentioned 的示例。


@Countably Infinite 和@Peter 重新计算:

插入两个表需要两个插入语句。但是,只要您有子表,在 SQL 中也是如此。这是一件很平常的事情。

UPDATE 可能需要两条语句,但某些品牌的 RDBMS 支持使用 JOIN 语法的多表 UPDATE,因此您可以在一条语句中完成。

查询数据时,如果只需要常用列的信息,可以通过查询media表来完成:

SELECT name, url FROM media WHERE media_id = ?

如果您知道自己正在查询电影,则可以通过单个连接获取特定于电影的信息:

SELECT m.name, v.release_date
FROM media AS m
INNER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?

如果您想获取给定媒体条目的信息,但不知道它是什么类型,则必须加入所有子类型表,知道只有一个这样的子类型表会匹配:

SELECT m.name, t.episode, v.release_date
FROM media AS m
LEFT OUTER JOIN tv_series AS t USING (media_id)
LEFT OUTER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?

如果给定的媒体是电影,那么t.* 中的所有列都将为NULL。

【讨论】:

  • 关于 JDBC 支持它的任何信息?即使使用了子类型的特殊注释,它是否只是将查询和更新传递给 DBMS?还是会有问题?
  • 不,您需要编写正确的 SQL。 JDBC 在查询重写方面做得很少。它不支持任何注释,只支持一些转义序列以帮助供应商兼容性。
  • 那么结果集将只有当前子类型未使用的属性的空值?以及如何通过 checked 属性读取子类型本身?
  • 在此架构中,没有列是 serialauto_increment,因此您完全在代码中管理 media.media_id 的值 - 对吧?媒体表中似乎有两个条目,例如media_id: 2, media_type: 2media_id: 2, media_type: 3。如果是这样,则不建议使用此查询:SELECT name, url FROM media WHERE media_id = ?
  • @hallvors,你说得对,我在十二年前写了上面的例子,我没有正确编码约束。可以单独在 id 列上使用主键,并添加第二个唯一键约束,以便其他子类型表中的 2 列外键可以引用这对列。你需要我编辑上面的代码来显示吗?
【解决方案2】:

考虑使用一个主要的基本数据表,并使用专门的信息从它延伸出来。

例如

basic_data
id int,
name character varying(100),
description text,
url character varying(255)


tv_series
id int,
BDID int, --foreign key to basic_data
season,
episode
airing


movies
id int,
BDID int, --foreign key to basic_data
release_data
budget

【讨论】:

  • 谢谢,我之前已经想到了,我遇到的唯一问题是它理论上可以在 basic_data 中的一个表中有多个条目(即可能有一个电视连续剧和电影中的条目)。如果没有更好的结果,我会这样做。
  • 这也是一种单向关系,因为只有专用表指向通用表。
  • 投赞成票,尽管我建议您对“basic_data”表要小心,不要使其过于笼统。如果它是“媒体”或类似的东西,那是有道理的。不过,不要试图将没有意义的东西强加进去。
  • 是的,这就是我想在下面说的,但显然用得太少了。下次我会更详细。
  • 拥有多个与 basic_data 相关的条目是关系数据库的整个 IDEA。而且它不是单向的——它是双向的,但是从基本表到叶表是一对多的关系。
【解决方案3】:

由于您标记了此 PostgreSQL,因此您可以查看 http://www.postgresql.org/docs/8.1/static/ddl-inherit.html,但请注意警告。

【讨论】:

    【解决方案4】:

    您正在寻找的东西在关系世界中被称为“不相交的子类型”。它们在语言级别的 sql 中不受支持,但或多或​​少可以是 implemented on top of sql

    【讨论】:

      【解决方案5】:

      您可以创建一个包含主要字段和一个 uid 的表,然后为每个特定情况创建具有相同 uid 的扩展表。要像单独的表一样查询这些,您可以创建视图。

      【讨论】:

        【解决方案6】:

        使用 Bill Karwin 建议的不相交子类型方法,您将如何执行 INSERT 和 UPDATE 而无需分两步执行?

        获取数据,我可以引入一个基于特定 media_type 连接和选择的视图,但我无法更新或插入该视图,因为它会影响多个表(我在这里说的是 MS SQL Server)。这可以在不执行两个操作的情况下完成吗 - 自然也不需要存储过程。

        谢谢

        【讨论】:

        • 好吧,我想,我可以使用一个代替触发器来完成这项工作,但这意味着我必须为我拥有的每个子类型编写这些代码。而且,我认为触发器在调试时是如此“秘密”......有没有更好的方法?
        【解决方案7】:

        问题已经很老了,但对于现代 postresql 版本,也值得考虑使用 json/jsonb/hstore 类型。 例如:

        create table some_table (
            name character varying(100),
            description text,
            url character varying(255),
            additional_data json
        );
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-07
          • 1970-01-01
          • 2017-12-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-26
          相关资源
          最近更新 更多