SQL数据库表中的多态性?

时间:2009-02-18 15:39:59

标签: sql postgresql normalization

我目前在我的数据库中有多个表,它们包含相同的“基本字段”,如:

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

但我有该基本表的多个特化,例如tv_series包含字段seasonepisodeairing,而movies } table有release_datebudget等。

现在首先这不是问题,但我想创建第二个表,名为linkgroups,带有这些专用表的外键。这意味着我会以某种方式将其自身标准化。

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

所以我现在正在寻找一种方法来在多个表之间“共享”主键,甚至更好:通过使用通用表和多个专用表来规范化它。

7 个答案:

答案 0 :(得分:27)

是的,问题是您只希望一个子类型的一个对象引用父类的任何给定行。从@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, 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评论:

INSERT到两个表需要两个insert语句。但是,只要你有子表,这在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。

答案 1 :(得分:8)

考虑使用一个主要的基本数据表,其中表格带有专门的信息。

实施例

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

答案 2 :(得分:3)

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

答案 3 :(得分:2)

你所寻找的是在关系世界中被称为“不相交的亚型”。它们在语言级别的sql中不受支持,但可以更多或更少implemented on top of sql

答案 4 :(得分:1)

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

答案 5 :(得分:1)

使用Bill Karwin建议的不相交子类型方法,如何在不必分两步的情况下执行INSERT和UPDATE?

获取数据,我可以引入一个基于特定media_type加入和选择的视图,但是AFAIK我无法更新或插入到该视图中,因为它会影响多个表(我在这里说的是MS SQL Server)。可以在不进行两次操作的情况下完成此操作 - 并且无需存储过程。

由于

答案 6 :(得分:1)

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

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