Рекомендации по работе с базами данных с несколькими похожими сущностями

У меня очень простой вопрос относительно 2 альтернатив, из которых я не знаю, какую выбрать. У меня есть объекты, которые могут быть "контактами". «Контакт» может иметь несколько адресов электронной почты, несколько телефонных номеров и несколько адресов. В моей модели данных я создаю контакт с сущностью, который имеет связь от 1 до n с электронной почтой, телефоном и адресом сущности. Теперь в таблице email есть только поля «email» и «комментарии», а телефон имеет аналогичную структуру «номер телефона» и «комментарии».

введите здесь описание изображения

«Лучше» хранить их в двух разных таблицах или мне создать одну таблицу, давайте назовем ее «подробно» или как-то еще, со столбцами «значение», «тип» и «комментарии» с типом, например. «электронная почта» или «номер телефона».

введите здесь описание изображения

Я использую L4, и с моделями Eloquent я ожидаю, что будет легко написать некоторые методы, которые дают мне ту же функциональность, что и с другими таблицами. Но мне кажется, что строки для разделения разных типов информации неверны. Такое ощущение, что тогда легче ошибиться. При активной загрузке я ожидаю, что не будет значительно больше запросов, даже если у меня есть 2 таблицы. К вашему сведению, количество строк в телефоне/электронной почте будет определенно ниже 10 000.

Имеет ли значение, какой тип я выберу? Что бы ты сделал? И почему?

Спасибо за помощь,

С уважением


person Matthias S    schedule 15.01.2014    source источник
comment
Вы вступили в нечто вроде религиозной войны. Способ понять проблему заключается в том, как будут использоваться данные. Вы всегда ищете контакт при ссылке на другие таблицы? Или вы что-то хотите связать с определенными типами контактов?   -  person Gordon Linoff    schedule 15.01.2014
comment
Добавлено в избранное по интересам.   -  person ʰᵈˑ    schedule 15.01.2014
comment
Лучше - субъективный термин. Как правило, вы обмениваете уровень нормализации на «простоту использования». У большинства людей есть служебный адрес, домашний адрес и три контактных номера — домашний, рабочий (прямой набор) и мобильный. «Нормально» (в отличие от «нормализованного») хранить все это в одной таблице.   -  person Strawberry    schedule 15.01.2014
comment
Проблема в том, что я должен предоставить возможность хранить «бесконечные» номера телефонов. Поэтому я не могу делать предположения о том, сколько существует телефонных номеров, и помещать их в таблицу адресов. В этом конкретном случае у меня есть «бизнес» и «человек», для обоих мне нужно иметь возможность хранить разные помеченные адреса, разные номера телефонов и адреса электронной почты.   -  person Matthias S    schedule 15.01.2014
comment
У большинства людей есть три телефонных номера. Может быть и так, но если вы запишите три телефонных номера в одну запись, то что вы будете делать, когда кто-то придет с четырьмя телефонными номерами? У меня было время, когда у меня было два мобильных телефона, рабочий и личный. Зачем создавать потенциальную головную боль, когда есть простое решение? Мне вспоминается система страхования, над которой я работал, где вместо отдельной записи на каждого члена семьи у них была одна большая запись с мужем, женой и местами для шести детей. Меня вызвали, когда у них появился клиент, у которого было восемь детей. Неужели никто этого не предвидел?   -  person Jay    schedule 15.01.2014


Ответы (2)


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

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

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

Но такого рода рассуждения могут быть доведены до нелепых крайностей. Вопрос на самом деле не в том, различны ли две вещи в реальном мире, а в том, различны ли они для целей нашей системы. Например, я не могу себе представить, что, потому что иногда адрес — это почтовый ящик, а иногда это улица, а иногда это номер квартиры и т. д., что у нас должны быть отдельные поля для каждой из этих вещей, а не просто «адресная строка 1». " и "адресная строка 2". Или что у нас должны быть отдельные поля для «имени шатенки» и «имени блондина», потому что, эй, они выглядят по-разному. что касается, все является "товаром", а пользователь говорит нет, нет, как вы можете говорить, что мебель - это то же самое, что и канцелярские товары? Но если мы делаем в системе запись названия, количества в наличии и цены , чем меня не волнует разница.

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

В таком случае, что вы собираетесь делать с телефонными номерами и адресами электронной почты? Вероятно, в основном пользователи вводят их, а затем отображают. Я легко могу представить себе систему, в которой вы даже не будете проверять, что есть что. Во время ввода данных есть раскрывающийся список для «типа контактной информации», а во время отображения вы отображаете тип контакта вместе со значением контакта, возможно, отсортированным по типу контакта. Если вы отправляете автоматические электронные письма, возможно, вы выбираете, где type='email'.

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

Кстати, если вы используете одну таблицу, вам нужен код, чтобы указать, какой это тип контакта. Я думаю, вы понимаете это. Я предлагаю вам создать справочную таблицу, содержащую коды и их определения, например, создать таблицу contact_type (первичный ключ contact_type_code char(2), contact_type_description varchar(40)), а не жестко кодировать типы контактов в программе. Или, что еще хуже, помещая описание типа контакта в каждую запись, поэтому иногда он говорит «электронная почта», а иногда «электронная почта», иногда «электронная почта» и, возможно, иногда «электронная почта» или «интернет». .

Извините за длинный бессвязный ответ.

person Jay    schedule 15.01.2014
comment
Спасибо за этот совет. Мое приложение довольно маленькое, но требования размыто объяснены и, кажется, меняются по желанию клиентов. Я думаю, что одну таблицу легче поддерживать - person Matthias S; 22.01.2014

Вот как я обычно создаю таблицы для адресов, контактов и электронной почты:

Примерами типов адресов могут быть «Домашний», «Почтовый», «Рабочий» и т. д.

address_types
    id                  varchar(15)(P)

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

addresses
    id                  unsigned int(P)
    address_type_id     varchar(15)(F address_types.id)
    line1               varchar(50)
    line2               varchar(50)
    city_id             unsigned int(F cities.id) Default NULL
    county_id           unsigned int(F counties.id) Default NULL
    zip                 varchar(6)
    zip4                char(4) Default NULL
    lat                 decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL     lon                     decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL

Вы можете или не можете хотеть такие вещи, как fips_number и столбцы, перечисленные после него. Это коды, которые использовались правительством США.

cities
    id                      unsigned int(P)
    state_id                unsigned int(F states.id)
    name                    varchar(50)
    lat                     decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL
    lon                     decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL
    fips_number             unsigned int // Default NULL
    census_code             unsigned int // Default NULL
    census_class_code       char(2)  // Default NULL
    gsa_code                unsigned int // Default NULL
    opm_code                unsigned int // Default NULL

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

cities_counties
    city_id             unsigned int \_ (P) (F cities.id)
    county_id           unsigned int /      (F counties.id)

Примерами типов контактов могут быть «Домашний», «Рабочий», «Пейджер», «Сотовый» и т. д.

contact_types
    id                  varchar(15)(P)

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

contacts
    id                  unsigned int(P)
    contact_type_id     varchar(15)(F contact_types.id)
    country_code        char(3) // Default to NULL
    area_code           char(3)
    exchange            char(3)
    station             char(4)
    extension           varchar(10) // Default to NULL

Это будет охватывать все округа, приходы и другие подобные географические области в штатах и ​​​​территориях США.

counties
    id                      unsigned int(P)
    state_id                unsigned int(F states.id)
    name                    varchar(50)
    fips_number             unsigned int // Default NULL

Подробнее о кодах стран см. в ISO 3166-1.

countries
    id                      char(2)(P)
    iso3                    char(3)(U)
    iso_num                 char(3)(U)
    name                    varchar(44)(U)

Примеры типов контактов могут быть «Дом», «Работа» и т. д.

email_types
    id                  varchar(15)(P)

Адреса электронной почты — вы можете разделить их, а можете и не разделить, как в моем примере.

emails
    id              unsigned int(P)
    email_type_id   varchar(15)
    mailbox         varchar(255)
    domain          varchar(255)

См. ISO 3166-2 для получения дополнительной информации о кодах штатов.

states
    id                      unsigned int(P)
    country_id              char(2)(F countries.id)
    code                    varchar(3)
    name                    varchar(45)
    fips_number             unsigned int // Default NULL

И затем, конечно, вы привязываете их к сущности:

customer_addresses
    id              unsigned int(P)
    customer_id     unsigned int(F customers.id)
    address_id      unsigned int(F addresses.id)

customer_contacts
    id              unsigned int(P)
    customer_id     unsigned int(F customers.id)
    contact_id      unsigned int(F contacts.id)

customer_emails
    id              unsigned int(P)
    customer_id     unsigned int(F customers.id)
    email_id        unsigned int(F emails.id)
person Benny Hill    schedule 15.01.2014
comment
Это намного сложнее, чем нужно большинству систем, над которыми я работал. Например, когда меня волнует, в каком округе находится город? Или какой код переписи для города? Если вы работаете в судоходном бизнесе или занимаетесь демографическими исследованиями, возможно, все это будет ценным, а в таком случае — крутым. Но для большинства систем гораздо проще просто иметь, например. текстовое поле произвольной формы для названия города. Встраивание такой сложности, если она вам не нужна, просто создаст вам проблемы, когда вы столкнетесь с делами, которые не соответствуют шаблону. Например, неамериканские телефонные номера не всегда разбиты на группы из 3, 3 и 4 цифр. - person Jay; 15.01.2014
comment
@Jay - на самом деле, этот дизайн появился благодаря более чем 15-летнему опыту доставки. Все телефонные номера в Северной Америке соответствуют шаблону 999-999-9999, точно так же, как все почтовые индексы состоят из 5 цифр (США и МХ) или 6 символов (CA). Однако ответ, как и многие ответы, относящиеся к дизайну базы данных, на самом деле задуман как отправная точка. Мы понятия не имеем, каковы потребности бизнеса Матиаса, лучшее, что мы можем сделать, это дать ему проверенный и верный совет. - person Benny Hill; 15.01.2014
comment
Обратите внимание, я сказал, что если вы занимаетесь доставкой... Так что для вас этот дизайн может иметь большой смысл. Но для большинства систем это много ненужной сложности. И да, все номера телефонов в Северной Америке — 999-999-9999, а номера телефонов на других континентах — нет. Я не пытался сказать, что ваш дизайн глупый или что-то в этом роде, просто я думаю, что вы должны воспринимать его в контексте, и вы должны понимать контекст. - person Jay; 15.01.2014
comment
Спасибо за обмен вашего опыта. Это, конечно, слишком подробно для моего приложения. Но это показывает мне, какой подход вы выбрали - разные таблицы для разных данных. Я думаю, что буду придерживаться одной таблицы (martinfowler.com/eaaCatalog/singleTableInheritance.html) . Наконец узнал, что это называется наследованием одной таблицы. - person Matthias S; 22.01.2014