2009-02-18 3 views
22

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

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

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

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

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

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

+0

Хорошо, я не уверен на 100%, что я читаю это право. У вас есть куча разрозненных таблиц, которые имеют общую структуру. Вы хотите добавить одну таблицу, которая ссылается на эти таблицы, но вы не можете использовать FK, потому что каждая таблица имеет свой собственный ПК. Если можно, я бы создал общую мастер-таблицу и покончил с этим. – Joe

+0

Да, но мой вопрос в том, как именно это самое элегантное –

ответ

24

Правильно, проблема в том, что вы хотите, чтобы только один объект одного подтипа ссылался на любую заданную строку родительского класса. Начиная с example заданной @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) 
); 

Это пример непересекающихся подтипами mentioned по г @ Mike.


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

ВСТАВИТЬ на две таблицы будут требовать две инструкции вставки. Но это также верно в SQL, когда у вас есть дочерние таблицы. Это обычная вещь.

UPDATE может потребовать два оператора, но некоторые марки RDBMS поддерживают многозадачный 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.

+0

Что-нибудь известно о его поддержке JDBC? Будет ли он просто передавать запросы и обновления в СУБД, даже если используются специальные аннотации для подтипов? Или будут проблемы? –

+0

Нет, вам нужно написать правильный SQL. JDBC делает очень мало в отношении перезаписи запросов. Он не поддерживает никаких аннотаций, а всего лишь несколько управляющих последовательностей, чтобы помочь совместимости с производителями. –

+0

Таким образом, набор результатов будет иметь только нулевые значения для атрибутов, не используемых текущими подтипами? И как будет считываться с самого подтипа через проверенный атрибут? –

1

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

8

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

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 
+0

Спасибо, я уже думал об этом раньше, единственная проблема, с которой я столкнулся, заключается в том, что теоретически это дает возможность иметь несколько записей в таблицах для одного в basic_data (т. е. может быть запись в tv_series и фильмы). Если ничего лучше не приходит, я делаю так. –

+0

А также это однонаправленная связь, так как только специализированные таблицы указывают на общую таблицу. –

+0

Голосование, хотя я бы посоветовал вам быть осторожным с таблицей «basic_data», чтобы не сделать его слишком общим. Если это «медиа» или что-то в этом роде, это имеет смысл. Не пытайтесь вложить в него что-то, что не имеет смысла. –

2

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

1

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

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

Благодаря

+0

Ну, я думаю, я мог бы использовать вместо триггера, чтобы выполнить эту работу, но это означает, что мне придется кодировать их для каждого подтипа, который у меня есть. А также, я думаю, что триггеры настолько «секретны» при отладке ... Есть ли лучший способ? – Peter

1

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

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