Проблемы базы данных SQL с дизайном таблицы адресной книги - PullRequest
2 голосов
/ 15 декабря 2008

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

Я могу создать n записей для каждого типа, который я хочу. Тип означает здесь данные, такие как «электронная почта», «адрес», «телефон» и т. Д.

У меня есть таблица с именем contact_profiles.

Это только две колонки:

id           Primary key
date_created DATETIME

А затем есть таблица с именем contact_attributes. Это немного сложнее:

id       PK
#profile (Foreign key to contact_profiles.id)
type     VARCHAR describing the type of the entry (name, email, phone, fax, website, ...) I should probably change this to a SET later.
value    Text (containing the value for the attribute).

Теперь я могу ссылаться на эти профили, например, из таблицы моего пользователя. Но отсюда я сталкиваюсь с проблемами.

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

Так что сейчас я бы получил что-то вроде

#profile type    value
1        email   name@domain.tld
1        name    Sebastian Hoitz
1        website domain.tld

Но было бы неплохо получить такой результат:

#profile email           name            website
1        name@domain.tld Sebastian Hoitz domain.tld

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

Так вы знаете, есть ли возможность конвертировать это динамически?

Если вам нужно лучшее описание, пожалуйста, дайте мне знать.

Ответы [ 6 ]

4 голосов
/ 15 декабря 2008

Вы заново изобрели дизайн базы данных под названием Entity-Attribute-Value . У этого дизайна есть много недостатков, в том числе обнаруженная слабость: очень трудно воспроизвести результат запроса в обычном формате с одним столбцом на атрибут.

Вот пример того, что вы должны сделать:

SELECT c.id, c.date_created,
 c1.value AS name,
 c2.value AS email,
 c3.value AS phone,
 c4.value AS fax,
 c5.value AS website
FROM contact_profiles c
 LEFT OUTER JOIN contact_attributes c1
  ON (c.id = c1.profile AND c1.type = 'name')
 LEFT OUTER JOIN contact_attributes c1
  ON (c.id = c1.profile AND c1.type = 'email')
 LEFT OUTER JOIN contact_attributes c1
  ON (c.id = c1.profile AND c1.type = 'phone')
 LEFT OUTER JOIN contact_attributes c1
  ON (c.id = c1.profile AND c1.type = 'fax')
 LEFT OUTER JOIN contact_attributes c1
  ON (c.id = c1.profile AND c1.type = 'website');

Вы должны добавить еще LEFT OUTER JOIN для каждого атрибута. Вы должны знать атрибуты во время написания запроса. Вы должны использовать LEFT OUTER JOIN, а не INNER JOIN, потому что нет способа сделать атрибут обязательным (эквивалент простого объявления столбца NOT NULL).

Гораздо эффективнее извлекать атрибуты по мере их сохранения, а затем писать код приложения для циклического прохождения набора результатов, создавая объект или ассоциативный массив с записью для каждого атрибута. Вам не нужно знать все атрибуты таким образом, и вам не нужно выполнять n -way соединение.

SELECT * FROM contact_profiles c
  LEFT OUTER JOIN contact_attributes ca ON (c.id = ca.profile);

Вы спросили в комментарии, что делать, если вам нужен этот уровень гибкости, если не использовать дизайн EAV? SQL не является правильным решением, если вам действительно нужна неограниченная гибкость метаданных. Вот несколько альтернатив:

  • Сохраните TEXT BLOB, содержащий все атрибуты, структурированные в формате XML или YAML.
  • Используйте решение для моделирования семантических данных, например Сезам , в котором любой объект может иметь динамические атрибуты.
  • Отказаться от баз данных и использовать плоские файлы.

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

1 голос
/ 15 декабря 2008

Если вы ограничиваетесь отображением одного адреса электронной почты, имени, веб-сайта и т. Д. Для каждого человека в этом запросе, я бы использовал подзапросы:

SELECT cp.ID profile
  ,cp.Name
  ,(SELECT value FROM contact_attributes WHERE type = 'email' and profile = cp.id) email
  ,(SELECT value FROM contact_attributes WHERE type = 'website' and profile = cp.id) website
  ,(SELECT value FROM contact_attributes WHERE type = 'phone' and profile = cp.id) phone
FROM contact_profiles cp

Если вы используете SQL Server, вы также можете посмотреть на PIVOT .

Если вы хотите показать несколько электронных писем, телефонов и т. Д., Учтите, что в каждом профиле их должно быть одинаковое количество, иначе у вас будут пробелы.

Я бы также выделил столбец типа. Создайте таблицу с именем contact_attribute_types, в которой будут храниться «электронная почта», «веб-сайт» и т. Д. Затем вы сохраните целочисленное значение contact_attribute_types.id в таблице contact_attributes.

0 голосов
/ 17 мая 2010

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

0 голосов
/ 09 мая 2009

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

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

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

0 голосов
/ 15 декабря 2008

Вы создаете представление для каждого типа контакта

Когда вам нужна вся информация, которую вы извлекаете из всей таблицы, когда вам нужно подмножество определенного типа контакта, вы извлекаете из вида.

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

Я реализовал похожий дизайн для нескольких систем малого / среднего размера, и у меня не было проблем.

Я что-то упустил? Это кажется тривиальным?

EDIT:

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

0 голосов
/ 15 декабря 2008

Вам нужно будет сгенерировать запрос как:

select #profile,
       max(case when type='email' then value end) as email,
       max(case when type='name' then value end) as name,
       max(case when type='website' then value end) as website
from mytable
group by #profile

Однако, это покажет только одно значение для каждого типа на #profile. Ваша СУБД может иметь функцию, которую вы можете использовать вместо MAX для объединения всех значений в виде строки, разделенной запятыми, или вы можете написать ее.

Такого рода модель данных лучше всего избегать по причинам, которые вы уже упомянули!

...