Дизайн базы данных: объекты с разными атрибутами

Я разрабатываю базу данных продуктов, в которой продукты могут иметь очень разные атрибуты в зависимости от их типа, но атрибуты фиксированы для каждого типа, а типы вообще не управляемы. Например.:

журнал: название, номер_выпуска, страницы, копии, дата_закрытия, дата_выпуска
веб-сайт: название, пропускная способность, просмотры, дата_от, дата_до

Я хочу использовать InnoDB и обеспечивать целостность базы данных настолько, насколько позволяет движок. Каков рекомендуемый способ справиться с этим?

Я ненавижу те конструкции, в которых таблицы имеют 100 столбцов, а большинство значений NULL, поэтому я подумал о чем-то вроде этого:

product_type
============

product_type_id INT
product_type_name VARCHAR

product
=======

product_id INT
product_name VARCHAR
product_type_id INT -> Foreign key to product_type.product_type_id
valid_since DATETIME
valid_to DATETIME

magazine
========

magazine_id INT
title VARCHAR
product_id INT -> Foreign key to product.product_id
issue_number INT
pages INT
copies INT
close_date DATETIME
release_date DATETIME

web_site
========

web_site_id INT
name VARCHAR
product_id INT -> Foreign key to product.product_id
bandwidth INT
hits INT
date_from DATETIME
date_to DATETIME

Это может справиться с каскадным удалением продукта, но... Ну, я не совсем уверен...


person Álvaro González    schedule 26.04.2010    source источник


Ответы (3)


Это классический объектно-ориентированный дизайн для несоответствия импеданса реляционных таблиц. Описанный вами дизайн таблицы известен как «таблица для каждого подкласса». Все три наиболее распространенных дизайна — это компромиссы по сравнению с тем, как на самом деле выглядят ваши объекты в вашем приложении:

  1. Таблица по классам бетона
  2. Таблица на иерархию
  3. Таблица на подкласс

Дизайн, который вам не нравится — «где таблицы имеют 100 столбцов, а большинство значений — NULL» — это 2. Одна таблица для хранения всей иерархии специализации. Это наименее гибкий вариант по разным причинам, в том числе — если вашему приложению требуется новый подкласс, вам нужно добавить столбцы. Описанный вами дизайн гораздо лучше подходит для изменений, потому что вы можете расширить его, добавив новую таблицу подклассов, описываемую значением в product_type.

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

Описанная вами конструкция вполне жизнеспособна. Вариант ниже показывает, как это могло бы выглядеть, если бы вы использовали инструмент ORM для выполнения операций CRUD. Обратите внимание, что идентификатор в каждой таблице подкласса ЯВЛЯЕТСЯ значением FK для родительской таблицы в иерархии. Хорошая ORM автоматически управляет правильной таблицей подклассов CRUD на основе значений дискриминатора только в product.id и product.product_type_id. Планируете ли вы использовать ORM или нет, ознакомьтесь с документацией присоединенных подклассов hibernate, хотя бы для того, чтобы увидеть принятые ими проектные решения.

product
=======

id INT
product_name VARCHAR
product_type_id INT -> Foreign key to product_type.product_type_id
valid_since DATETIME
valid_to DATETIME

magazine
========

id INT -> Foreign key to product.product_id
title VARCHAR
..

web_site
========

id INT -> Foreign key to product.product_id INT
name VARCHAR
..
person mhanney    schedule 26.04.2010
comment
Хороший обзор трех возможных дизайнов, спасибо. Все ответы были превосходны, но я могу выбрать только один <:-) - person Álvaro González; 27.04.2010

Кажется, вы на правильном пути, за исключением того, что вам, возможно, придется рассмотреть разницу между «продуктом» и тем, что часто называют «единицей хранения» (SKU). Является ли коробка канцелярских скрепок (определенного типа) на 25 штук тем же «продуктом», что и коробка на 50 штук? С точки зрения магазина или любой системы инвентаризации различие имеет значение; в некоторых случаях, действительно, простое различие в упаковке того, что в противном случае равно количеству одного и того же базового «продукта», может дать вам разные SKU для отслеживания.

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

person Alex Martelli    schedule 26.04.2010

На самом деле это стандартный способ «принудить» своего рода объектно-ориентированный дизайн в классической СУБД.

Все «общие» атрибуты помещаются в основную таблицу (например, цена, если она поддерживается на уровне таблицы продуктов, легко может быть частью основной таблицы), а особенности — в подтаблице.

Теоретически, если у вас есть под-подтипы (например, журналы могут быть подтипами ежедневных газет и 4-цветных периодических изданий, возможно, с периодическими изданиями, имеющими интервал даты для срока годности), вы также можете добавить один или несколько подуровней...

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

С другой стороны, обычная операция, такая как удаление элемента (я бы предложил логическое удаление, установка флага «true» в главной таблице), будет выполняться один раз для каждого типа подтипа.

Во всяком случае, пойти на это. И, возможно, погуглите «Объектно-ориентированные сопоставления РСУБД» или что-то подобное для полного обсуждения< /сильный>.

person p.marino    schedule 26.04.2010