2014-10-15 3 views
6

У меня проблема с проектированием базы данных (SQL/MySQL). Предположим, у нас есть пользователь , пользователь может иметь много друзей и много сообщений и заполнить некоторые данные о себе.Дизайн базы данных - отношения против свойств

Совершенно очевидно, что для friends нам нужна одна pivot_table для отношения n: n, для posts нам нужно создать одну дополнительную таблицу с отношением user_id (1: n).

Для этого нам нужны users, user_friends и posts столы. Это очевидно. Вот как следует обращаться с отношениями.

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

name - text 
description - text 
marital status - select only one from list 
favourite colour - select only one from list 
hobby - select up to 3 from list 

Для текстовых полей (название, описание) это действительно очевидно, мы просто создаем VARCHAR/текстовые столбцы в users таблицы и это все.

Общий вопрос:: как обрабатывать другие поля (выбираемые из списков)? Должен ли я создавать отношения для них или, возможно, следует создавать с ними стандартные столбцы данных?

На мой взгляд, нет смысла создавать таблицы отношений для этого, потому что, используя списки (выберите), мы ограничиваем пользователя, когда он может фактически вставить в базу данных. В теории мы могли бы позволить пользователю вручную вводить в качестве любимого цвета свой цвет (например, red, и если он что-то неправильно набирает, например, reds, мы бы сравнили его список разрешенных colours). То же самое можно сказать и о гендерных вопросах. По моему мнению, нет смысла создавать дополнительную таблицу, когда мы держим только женщину и человека и создаем для нее отношение.

Первый DB дизайн:

Я мог бы, например, создать следующие столбцы для свойств:

marital_status - int 
fav_colour - int 
hobby_1 - int 
hobby_2 - int 
hobby_3 - int 

И есть еще один стол (или даже простой массив в PHP или другом языке), где я храню, что значение 1 для fav_colour, например, красное, значение 2 для хобби - это музыка и т. д. (неважно, как я храню эти значения здесь - я мог бы также использовать для этого тип enum).

Для меня преимущества такого отношения не создают много отношений, которые на самом деле скорее являются свойствами, а не отношениями (как я уже упоминал выше), поэтому меньше работы + проще получать информацию о пользователе - вам не нужно использовать какие-либо объединения что было бы важно, если у вас есть для пользователя, например, 20 или 100 таких свойств, и я могу легко искать в пользовательской таблице. Недостатки также совершенно очевидны - данные не нормализованы, для любого множественного выбора (например, для хобби) мне нужно создать 3 столбца, и если в будущем я решите, что пользователь может выбрать не 1 цвет, а 2 или 3, мне нужно будет добавить 2 дополнительных столбца.

Альтернативная конструкция DB:

создать дополнительные таблицы: colours, hobbies, marital_statuses и создать 3 Повороты таблицы: user_colours, user_hobbies, user_marital_statuses. Недостатки: многие присоединяются. Преимущества - если бы я создал 3 дополнительные сводные таблицы, я бы мог легко разрешить пользователю выбирать до 10 цветов, и мне вообще не нужна база редизайна. Но также возникают и недостатки - сложный поиск, большая работа, много объединений.

Детальный вопрос

Так, чтобы подвести итог - какое решение было бы лучше, если предположить:

  1. я бы, вероятно, не изменить максимальное количество одного свойства (если я решил позволить максимум 3 хобби, это вероятно, никогда не изменится)
  2. Списки выбора для многих полей будут относительными (для большинства из них менее 10)
  3. Мне нужно много искать в такой базе данных. Кто-то, например, хочет найти пользователя, у которого fav_colour установлен на красный, и у него есть музыка для хобби.

Если есть какие-либо другие решения или преимущества/недостатки, которые вы видите, я ценю поделиться со мной.

+1

Вот еще один вариант. Создайте таблицу атрибутов с атрибутомName, attributeType, attributeValue, userId. Это позволит вам добавить столько атрибутов пользователю, сколько захотите. Избегайте необходимости вносить изменения схемы в любое время, когда вы думаете о новом бит информации, который вы хотите. – paqogomez

+4

@paqogomez: это анти-паттерн, называемый «сущностью-атрибутом-значением», но, вероятно, решение, которое усугубляет здесь меньше. Другой вариант - хранить «динамические атрибуты» как JSON или XML-документ. Но это очень сложно обрабатывать их в SQL. Третьим вариантом может быть обновление до Postgres и использование функций NoSQL Postgres, таких как тип данных «ключ/значение» «hstore» или встроенная поддержка JSON. –

+0

@a_horse_with_no_name. На самом деле у меня был комментарий, который предложил параметр NoSQL, но удалил его , Я подумал, что это не направление, на которое OP хочет пойти. Это именно тот тип данных, который был создан для NoSQL. – paqogomez

ответ

1

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

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

Я рекомендую использовать sql CHECK ограничение. С его помощью вы можете управлять возможными значениями поля. Например:

CREATE TABLE users 
(
Name varchar(255) NOT NULL, 
Description varchar(255), 
Marital_Status varchar(10) NOT NULL, 
Color varchar(10) NOT NULL, 
CONSTRAINT chk_Color CHECK (Color in ('Red', 'Blue', 'Green', 'Orange')), 
CONSTRAINT chk_Marriage CHECK (Marital_Status in ('Single', 'Married', 'Divorced')) 
) 

У меня нет синтаксиса, проверяющего этот оператор DDL, поэтому он может содержать ошибки пунктуации. Кроме того, синтаксис может отличаться для вашей конкретной СУБД. Я думаю, что это должно работать для MySQL.

+0

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

1

Если пользователи могут часто менять любимые цвета/хобби, я бы использовал таблицы lookup, в моем примере я назову их decode столами. Все отношения между user/hobbies и user/colors будут найдены в таблице decode.

Поскольку у вас может быть только 1 marital status, это легко обрабатывать, это отношения от одного до многих.

Создать таблицу Marital_Status с 2 полями, Id (pk) и Status(varchar(n)) Таблица decode не требуется для поиска marital status.

Теперь я бы рекомендовал создать таблицу для хранения colors и таблицу для hobbies. То же самое мы сделали marital status.

Hobbies 

HobbyId, Hobby 

Colors 
ColorId, Color 

Всякий раз, когда вам нужно добавить/удалить новый hobby/color сделать это в этих decode таблицах.

Это зависит от вас, хотите ли вы использовать 1 decode таблицу для каждой взаимосвязи или много то есть. Hobby_Decode and Color_Decode и т.д.

Я объясню сценарий использования 1.

Создать таблицу декодирования со следующими полями ...

Decode

Item_Type varchar(n) --Мы подтолкнет либо Hobby или Color в этой области

UserId INT --self пояснениях, содержит идентификатор пользователя на "поиск"

LookupId - будет содержать идентификаторы либо Hobby, либо Color

Позвольте мне создать образец данных, и мы сработаем.

Hobbies table данные

| HobbyId | Hobby 

     1  Studying 
     2  Doing Drugs 
     3  Drinking  

Colors table данных

| ColorId | Color 

    1  Red 
    2  Blue 

В то время как мы в этом, вот наша таблица пользователей.

Users

| UserId | Name 

     1  Marcin 
     2  CSharper 

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

Decode

| Item_Type| UserId | LookUpId 

    'Hobby'  2  2 
    'Hobby'  2  3 
    'Color'  2  1 
    'Hobby'  1  1 
    'Color'  1  2  

Глядя Декодеры таблицы на самом деле не говорит нам ничего. Как только мы присоединяемся к нашему decode столу до colors/hobbies, это будет очевидно.

Если вы хотите посмотреть все мои увлечения и мои любимые цвета запрос будет выглядеть этот

Примечание: это синтаксис SQL Server не MySQL.

--Pull Hobbies 
Select u.Name, dH.Item_Type as 'Favorite', h.Hobby as 'Item' 
from User u 
inner join decode dH on dH.UserId = u.UserId 
        and dH.Item_Type = 'Hobby' 
inner join Hobby h on h.HobbyId = dH.LookUpId 
where u.UserId = 2 

--Union in Colors 
Union 

Select u.Name, dH.Item_Type as 'Favorite', h.Hobby 'Item' 
from User u 
inner join decode dC on dH.UserId = u.UserId 
        and dH.Item_Type = 'Color' 
inner join Color c on c.ColorId = dH.LookUpId 
where u.UserId = 2 

Ваш выход будет выглядеть

| Name | Favorite |  Item 

    CSharper   Hobby   Drinking 
    CSharper   Hobby   Doing Drugs 
    CSharper   Color   Red 

Если это установка, как это, чем это очень легко изменить/обновить народы любимые увлечения и цвета. Стол decode будет обрабатывать все это. Это просто требует простой записи или удаления этой таблицы. А также таким образом, Пользователь может иметь бесконечное количество любимых хобби и цветов, поскольку это таблица декодирования, которая управляет этим, а не определение таблицы Users.

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

Select u.Name 
from User u 
inner join decode d on d.UserId = u.UserId 
inner join Hobby h on h.HobbyId = d.LookUpId and d.Item_Type = 'Hobby' 
inner join Color c on C.ColorId = d.LookUpId and d.Item_Type = 'Color' 
where h.Hobby = 'drinking' and c.Color = 'blue' 

Выполнение подобных операций вполне приемлемо.

+0

Хм, разве это не сложно? Что делать, если я хочу получить все свойства для одного пользователя. Здесь у нас есть цвет и до 3 хобби. Но что, если есть более 20 таких «отношений»? Разве это не было бы сложно и слишком сильно повлиять на скорость? –

+0

Нет, совсем нет, я работаю в финансовой компании и доверяю мне. Запросы тяжелы, чтобы сказать наименее, и это работает отлично. Вы можете бросить индекс на UserId и Item_type, чтобы ускорить работу даже больше. Вы можете разделить его и использовать несколько таблиц декодирования, если хотите, это, вероятно, было бы проще и понятнее в вашем случае. Если ваше высказывание о том, что Пользователь может иметь разные количества избранного X, я не знаю более простого решения. Изменение таблицы пользователей постоянно для включения нескольких любимых X не было бы оптимальным. Таблица декодирования для хранения каждого отношения будет работать. – CSharper

1

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

create table users (
    user_id bigint unsigned not null auto_increment primary key, 
    name varchar(255) not null, 
    description varchar(255), 
    marital_status enum('single', 'married'), 
    favorite_color enum('red', 'green', 'blue'), 
    hobby1 enum('painter', 'doctor', 'lawyer'), 
    hobby2 enum('painter', 'doctor', 'lawyer'), 
    hobby3 enum('painter', 'doctor', 'lawyer') 
); 

Чтобы вставить значение: insert into table users (name, marital_status) values ('Jack', 'single');

Это утверждение подведет: insert into table users (name, marital_status) values ('Jack', 'abcd');

Изменение списка является простая и быстрая операция: alter table users modify marital_status enum('divorced', 'single', 'married');

+0

Хорошо, но как насчет проверки данных? Как проверить, какие значения допустимы? Например, пользователь заполнит цвет abc. Можно ли запросить поле enum для получения разрешенных значений? И дополнительный вопрос - как хранить переводы для них? Предполагая, что мы используем более одного языка на странице, имена должны дублироваться в другой таблице/данных для хранения перевода. –

+0

Вы можете использовать «show create table» tablename »или запросить [information_schema] (http: // stackoverflow. com/questions/2350052/how-can-i-get-enum-possible-values-in-mysql-database), чтобы получить разрешенные значения в перечислении. Для переводов вы хотите иметь независимые от языка значения, которые вы используете в перечислении и переводах, которые будут отдельной таблицей. –

0

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

Но для меня, будет идти с 5 таблиц users, marital_status, colours, hobbies, user_hobbies

CREATE TABLE users (
    user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL, 
    description VARCHAR(255), 
    marital_status INT, 
    fav_colour INT 
) 

CREATE TABLE marital_status (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL 
) 

CREATE TABLE colours (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL, 
    code VARCHAR(7) 
) 

CREATE TABLE hobbies (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) NOT NULL 
) 

CREATE TABLE user_hobbies (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    user_id BIGINT, 
    hobby_id INT 
) 

Для сводных таблиц, я хотел бы предложить создать/заселить их отдельно от приложения, например, с помощью команды строка или очередь сообщений (либо с использованием функции crontab)

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