Полиморфизм в таблицах базы данных SQL? - PullRequest
24 голосов
/ 18 февраля 2009

В настоящее время в моей базе данных есть несколько таблиц, которые состоят из одних и тех же «базовых полей», таких как:

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

Но у меня есть несколько специализаций этой базовой таблицы, например, у tv_series есть поля season, episode, airing, в то время как у таблицы movies есть release_date, budget и т.д.

Теперь поначалу это не проблема, но я хочу создать вторую таблицу с именем linkgroups с внешним ключом для этих специализированных таблиц. Это означает, что мне нужно как-то нормализовать это внутри себя.

Один из способов решения этой проблемы, о котором я слышал, - это нормализовать ее с помощью таблицы key-value -pair, но мне не нравится эта идея, поскольку она является своего рода схемой «база данных в базе данных», У меня нет способа требовать определенных ключей / полей или специального типа, и было бы очень сложно получить и упорядочить данные позже.

Итак, я ищу способ «поделиться» первичным ключом между несколькими таблицами или, что еще лучше, способ его нормализовать с помощью общей таблицы и нескольких специализированных таблиц.

Ответы [ 7 ]

27 голосов
/ 18 февраля 2009

Правильно, проблема в том, что вы хотите, чтобы только один объект одного подтипа ссылался на любую данную строку родительского класса. Начиная с примера , заданного @Jay S, попробуйте следующее:

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.


Re комментарии @Counbly Infinite и @Peter:

Для вставки в две таблицы потребуется два оператора вставки. Но это также верно в SQL каждый раз, когда у вас есть дочерние таблицы. Это обычная вещь.

UPDATE может потребовать два оператора, но некоторые бренды СУБД поддерживают UPDATE для нескольких таблиц с синтаксисом JOIN, поэтому вы можете сделать это одним оператором.

При запросе данных вы можете сделать это, просто запросив таблицу 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.

8 голосов
/ 18 февраля 2009

Рассмотрите возможность использования основной таблицы базовых данных с таблицами, расширяющимися за ее пределами со специализированной информацией.

Ex.

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
3 голосов
/ 19 февраля 2009

Поскольку вы пометили этот PostgreSQL, вы можете посмотреть на http://www.postgresql.org/docs/8.1/static/ddl-inherit.html, но остерегайтесь предостережений.

2 голосов
/ 18 февраля 2009

То, что вы ищете, называется «непересекающимися подтипами» в реляционном мире. Они не поддерживаются в SQL на уровне языка, но могут быть более или менее реализованы поверх SQL .

1 голос
/ 05 мая 2015

Вопрос довольно старый, но для современных версий postresql также стоит рассмотреть использование типа json / jsonb / hstore. Например:

create table some_table (
    name character varying(100),
    description text,
    url character varying(255),
    additional_data json
);
1 голос
/ 13 мая 2010

Используя подход дизъюнктных подтипов, предложенный Биллом Карвином, как бы вы делали INSERT и UPDATE, не делая это в два этапа?

Получая данные, я могу представить представление, которое объединяется и выбирается на основе определенного типа media_type, но AFAIK Я не могу обновить или вставить это представление, поскольку оно влияет на несколько таблиц (здесь я говорю о MS SQL Server). Может ли это быть сделано без двух операций - и без хранимой процедуры, естественно.

Спасибо

1 голос
/ 18 февраля 2009

Вы можете создать одну таблицу с основными полями плюс uid, а затем таблицы расширений с одинаковыми uid для каждого конкретного случая. Для запроса этих отдельных таблиц вы можете создать представления.

...