2010-07-13 4 views
23

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

Например, продукт может быть книга (в этом случае мне нужно было бы метаданные, которые относятся к этой книге, как ISBN, автор и т.д.), или это может быть бизнес-список (который имеет различные метаданные) ..

Как мне это решить?

+7

Примечание: Если в базе данных хранится информация о книгах, то подробными атрибутами конкретной книги будут «данные», а не «метаданные». Метаданные будут представлять собой данные о самом механизме хранения, например, тот факт, что Book.Title является невалютным nvarchar (255). Если, однако, данные были сохранены в книге (например, альманахе), тогда информация о самой книге (например, ISBN и т. Д.) Будет метаданной. :-) –

ответ

34

Это так называемый шаблон наблюдения.

alt text http://www.damirsystems.com/dp_images/observation_model_3.png

Три объекта, для примера

Book 
Title = 'Gone with the Wind' 
Author = 'Margaret Mitchell' 
ISBN = '978-1416548898' 

Cat 
Name = 'Phoebe' 
Color = 'Gray' 
TailLength = 9 'inch' 

Beer Bottle 
Volume = 500 'ml' 
Color = 'Green' 

Это как таблицы может выглядеть следующим образом:

Entity 
EntityID Name   Description 
    1  'Book'   'To read' 
    2  'Cat'    'Fury cat' 
    3  'Beer Bottle'  'To ship beer in' 

.

PropertyType 
PropertyTypeID Name  IsTrait   Description 
    1   'Height'  'NO'  'For anything that has height' 
    2   'Width'  'NO'  'For anything that has width' 
    3   'Volume'  'NO'  'For things that can have volume' 
    4   'Title'  'YES'  'Some stuff has title' 
    5   'Author'  'YES'  'Things can be authored' 
    6   'Color'  'YES'  'Color of things' 
    7   'ISBN'  'YES'  'Books would need this' 
    8   'TailLength' 'NO'  'For stuff that has long tails' 
    9   'Name'  'YES'  'Name of things' 

.

Property 
PropertyID EntityID PropertyTypeID  
    1   1    4  -- book, title 
    2   1    5  -- book, author 
    3   1    7  -- book, isbn 
    4   2    9  -- cat, name 
    5   2    6  -- cat, color 
    6   2    8  -- cat, tail length 
    7   3    3  -- beer bottle, volume 
    8   3    6  -- beer bottle, color 

.

Measurement 
PropertyID  Unit  Value 
    6   'inch'  9   -- cat, tail length 
    7   'ml'  500   -- beer bottle, volume 

.

Trait 
PropertyID   Value 
    1   'Gone with the Wind'  -- book, title 
    2   'Margaret Mitchell'  -- book, author 
    3   '978-1416548898'   -- book, isbn 
    4   'Phoebe'     -- cat, name 
    5   'Gray'     -- cat, color 
    8   'Green'     -- beer bottle, color 

EDIT:

Jefferey поднял правильную точку (см комментарий), так что я буду расширять ответ.

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

Время для примера, но во-первых, чтобы упростить задачу, я сглажу модель в виде.

create view vModel as 
select 
     e.EntityId 
    , x.Name as PropertyName 
    , m.Value as MeasurementValue 
    , m.Unit 
    , t.Value as TraitValue 
from Entity   as e 
join Property   as p on p.EntityID  = p.EntityID 
join PropertyType  as x on x.PropertyTypeId = p.PropertyTypeId 
left join Measurement as m on m.PropertyId  = p.PropertyId 
left join Trait  as t on t.PropertyId  = p.PropertyId 
; 

Чтобы использовать пример Jefferey от комментария

with 
q_00 as (-- all books 
    select EntityID 
    from vModel 
    where PropertyName = 'object type' 
     and TraitValue = 'book' 
), 
q_01 as (-- all US books 
    select EntityID 
    from vModel as a 
    join q_00 as b on b.EntityID = a.EntityID 
    where PropertyName = 'publisher country' 
     and TraitValue = 'US' 
), 
q_02 as (-- all US books published in 2008 
    select EntityID 
    from vModel as a 
    join q_01 as b on b.EntityID = a.EntityID 
    where PropertyName  = 'year published' 
     and MeasurementValue = 2008 
), 
q_03 as (-- all US books published in 2008 not discontinued 
    select EntityID 
    from vModel as a 
    join q_02 as b on b.EntityID = a.EntityID 
    where PropertyName = 'is discontinued' 
     and TraitValue = 'no' 
), 
q_04 as (-- all US books published in 2008 not discontinued that cost less than $50 
    select EntityID 
    from vModel as a 
    join q_03 as b on b.EntityID = a.EntityID 
    where PropertyName  = 'price' 
     and MeasurementValue < 50 
     and MeasurementUnit = 'USD' 
) 
select 
     EntityID 
    , max(case PropertyName when 'title' than TraitValue else null end) as Title 
    , max(case PropertyName when 'ISBN' than TraitValue else null end) as ISBN 
from vModel as a 
join q_04 as b on b.EntityID = a.EntityID 
group by EntityID ; 

Это выглядит сложнее писать, но при ближайшем рассмотрении вы можете заметить закономерность в КТР.

Теперь предположим, что у нас есть стандартный фиксированный схема, где каждое свойство объекта имеет свой собственный столбец. Запрос будет выглядеть примерно так:

select EntityID, Title, ISBN 
from vModel 
WHERE ObjectType  = 'book' 
    and PublisherCountry = 'US' 
    and YearPublished = 2008 
    and IsDiscontinued = 'no' 
    and Price   < 50 
    and Currency   = 'USD' 
; 
+0

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

+4

Это очень плохая идея, и это приведет к дальнейшим проблемам в будущем. Пожалуйста, не делайте этого. –

+0

Например, попробуйте получить все книги от американских издателей академических книг, опубликованных в 2008 году, которые не были прекращены и стоят менее 50 долларов США. Удачи! В правильно разработанной реляционной базе данных это 2-минутная задача. –

2

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

4

Вы могли бы пойти со схемой менее подхода:

Держите метаданные в столбце Текст как объект JSON (или другой сериализации, но JSON лучше причинам вскоре объяснил).

Преимущества этого метода:

  1. Меньше запросов: вы получите всю информацию в одном запросе, нет необходимости «в направленности» запросов (чтобы получить мета-мета-данные) и присоединяется.

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

  3. Поскольку это JSON, вам не нужна дополнительная обработка на вашем сервере. Ваша веб-страница (я предполагаю, что это веб-приложение) просто читает JSON, как есть, из вашего веб-сервиса, и все, вы можете использовать JSON-объект с javascript, как вам нравится.

Проблема:

  1. Потенциально неиспользуемое пространство, если у вас есть 100 книг с тем же автором, автор таблицей с всеми книгами, имеющими только author_id более экономично пространство мудрым.

  2. Необходимо реализовать индексы. поскольку ваши метаданные являются объектами JSON, у вас нет индексов сразу. Но довольно просто реализовать конкретный индекс для конкретных метаданных, которые вам нужны. например, вы хотите индексировать по автору, поэтому вы создаете таблицу author_idx с author_id и item_id, когда кто-то ищет автора, вы можете посмотреть эту таблицу и сами элементы.

В зависимости от масштаба это может быть излишним. на меньшем масштабе объединения будут работать очень хорошо.

13

Я не собирался отвечать, но сейчас принято решение имеет очень плохая идея. Реляционная база данных никогда не должна использоваться для хранения простых пар атрибут-значение. Это вызовет массу проблем в будущем.

Лучший способ справиться с этим - создать отдельную таблицу для каждого типа.

Product 
------- 
ProductId 
Description 
Price 
(other attributes common to all products) 

Book 
---- 
ProductId (foreign key to Product.ProductId) 
ISBN 
Author 
(other attributes related to books) 

Electronics 
----------- 
ProductId (foreign key to Product.ProductId) 
BatteriesRequired 
etc. 

Каждая строка каждой таблицы должна представлять собой суждение о реальном мире, а также структуру таблиц и их ограничения должны отражать реальности, которые он представляет. Чем ближе вы сможете достичь этого идеала, тем чище будут данные, и тем легче будет делать отчетность и расширять систему другими способами. Он также будет работать более эффективно.

+0

Действительно, мне тоже нравится супертип-подтип - проблема в том, что происходит, когда количество таблиц подтипа переходит в тысячи? Как обрабатывать случаи, когда новые типы добавляются на лету? Что было бы рекомендовано в таком случае? Собственное хранилище XML или ... –

+0

Мне очень интересно посмотреть, что вы думаете о вопросе Дамира выше. – bukzor

+1

@ bukzor, @Damir Sudarevic - Действительно, я должен был ответить на этот вопрос, и я сожалею, что этого не делал. Ответ заключается в том, что база данных должна быть разработана с учетом реальной ситуации, которая известна. Если новые «типы» должны быть добавлены, то только согласованные между ними типы могут быть реляционно смоделированы. Может быть вызвана какая-то система атрибутов/значений, но только для тех вещей, которые являются «мягкими». В принципе, если что-то может быть изменено пользователем системы, а не программистом, то оно должно храниться как данные, а не как структура. –

2

В таком роде проблема, у вас есть три варианта:

  1. Создать таблицу с «общими» столбцами.Например, если вы продаете как книги, так и тостеры, вполне вероятно, что у ваших тостеров нет ISBN и названия, но у них все еще есть какой-то идентификатор и описание продукта. Так дайте Полям родовых имена, как «product_id» и «описание», и книги product_id является ISBN, для тостеров его номер детали производителя и т.д.

Это работает, когда сущности реального мира все обрабатываются таким же образом, по крайней мере, по большей части, и поэтому должны иметь, если не «те же» данные, по меньшей мере, аналогичные данные. Это разрушается при наличии реальных функциональных различий. Например, если для тостеров мы вычисляем ватты = вольт * усилители, вполне вероятно, что для книг нет соответствующего расчета. Когда вы начинаете создавать поля pages_volts, содержащие количество страниц для книг и напряжение для тостеров, все вышло из-под контроля.

  1. Используйте схему недвижимости/стоимости, такую ​​как Дамир. Смотрите мой комментарий на его пост за плюсы и минусы.

  2. Что я обычно предлагаю - это тип/тип подтипа. Создайте таблицу для «продукта», которая содержит код типа и общие поля. Затем для каждого из настоящих типов - книг, тостеров, кошек и т. Д. - создайте отдельную таблицу, связанную с таблицей продуктов. Затем, когда вам нужно выполнить обработку, специфичную для книги, обработайте таблицу книг. Когда вам нужно выполнить общую обработку, обработайте таблицу продуктов.

Смежные вопросы