Проект нормализации базы данных - одна или несколько таблиц - PullRequest
3 голосов
/ 12 июля 2010

Должно ли это быть представлено в базе данных как 1 таблица или 3 таблицы? У меня и моего друга разные мнения на этот счет, поэтому я хотел бы увидеть общие взгляды на это. (Может быть, это должно быть голосование за любое решение?)

Create Table Order
// Basic fields of the table
 - ID (Primary key)
 - CustomerID  (integer, with a FK)
 - Quantity
 - ProductID  (integer, with a FK)

 // Then depending on user selection, either these fields need to be specified 
 // (could be factored out to a separate table):
 {
 - InternalAccountID (integer, with a FK)
 - InternalCompanyID (integer, with a FK)
 }

 // Or these (could be factored out to a separate table):
 {
 - ExternalAccountNumber (free text string)
 - ExternalCompanyName (free text string)
 - ExtraInformation (free text string)
 }

1 подход к столу:

Плюсы:

  • производительность (одна вставка вместо двух, проверка FK, нет соединений)
  • , вероятно, занимает меньше места (дополнительные таблицы имеют накладные расходы + индексы + дополнительное поле ID)
  • один стол вместо трех
  • вряд ли оправдано разделение на новые таблицы только для 2 + 3 полей (или что?)

Минусы:

  • Обнуляемые поля
  • Потенциально дополнительный столбец типа (можно пропустить)
  • Перерывы 3NF (?)

За и против любезно просили, а также личные мнения. :)

РЕДАКТИРОВАТЬ: Я пытался упростить пример, используя другие объекты, чем я на самом деле использую, поэтому любые предложения по изменению модели действительно не помогут мне. То есть сфокусируйтесь на технических аспектах больше, чем на предметной модели, пожалуйста.

Ответы [ 7 ]

3 голосов
/ 30 июля 2010

Надеюсь, это говорит само за себя.

order_model_v1

3 голосов
/ 12 июля 2010

Мое мнение таково, что если

 // Then depending on user selection, either these fields need to be specified 
 // (could be factored out to a separate table):
 {
 - InternalAccountID (integer, with a FK)
 - InternalCompanyID (integer, with a FK)
 }

 // Or these (could be factored out to a separate table):
 {
 - ExternalAccountNumber (free text string)
 - ExternalCompanyName (free text string)
 - ExtraInformation (free text string)
 }

всегда 1: 1 с ордером (то есть, вы не можете иметь 3 идентификатора аккаунта), затем оставьте его как одну таблицу. Чтобы решить проблему с нулевым значением, вы можете добавить еще один столбец с именем InternalCustomer (логическое значение) или CustomerType (varChar), который можно использовать для определения внутреннего или внешнего клиента, чтобы узнать, какой из двух наборов полей следует искать для конкретный клиент.

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

0 голосов
/ 03 августа 2010

Связана ли информация учетной записи с клиентом до того, как он сможет сделать заказ (т. Е. У вас есть другая таблица, где вы отслеживаете, какие идентификаторы учетной записи могут использовать данные CustomerID)?Можете ли вы абстрагировать все учетные записи в достаточно унифицированную схему (такую, которая может иметь несколько нулей), если у вас есть один универсальный AccountId (суррогатный ключ), а затем в таблице Account есть 3 поля varchar и одно, которое отслеживает тип учетной записи (используетсядля выставления счетов и т. д.)?

Если вы можете сделать это, то ваш заказ отслеживает только один AccountID, поскольку заказ (как организация) действительно не заботится о том, какой метод оплаты был использован - он заботится только о том, что он является законным/ существующий / утвержденный AccountId для этого пользователя.Все остальное - это, так сказать, чужое дело (выставление счетов или проверка средств и т. Д.), И этому подразделению и его обработке в любом случае потребуется больше данных.

Это делает ваш Заказ чистым и свободным от нуля и облегчает разделение интересов по мере необходимости.ну.

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

Итак:

 Table Order (
     - OrderId
     - Quantity
     - ProductId
     - DiscountId -- sonner or latter :-)
     - AccountId
     - PaymentStatus -- probaly FK as well or predefined constant
 )

 Table Account (
     - AccountId
     - BillingInfo  -- akka ext acct number as text
     - PrincialName -- akka ext company name, some equivalent for internal acct-s
     - AdditionalData
 )
0 голосов
/ 03 августа 2010

Я не пурист, так что 3nf хорош, когда это имеет смысл ... но вам не нужно принимать это как должное, что это всегда будет.

С прагматической точки зрения, какова вашаЦель?Ваш список плюсов / минусов - хорошее начало.Я бы добавил еще несколько идей в список - по вашему усмотрению.

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

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

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

...

Я думаю, что в целом я выбираю масштабируемую систему, которая в данном случае можетзначит 3nf.С 1 таблицей легче работать в очень узкой области, но если что-то изменится, вы будете иметь дело с тем, «Как разделить эту таблицу на 3nf-таблицы, имеющие правильную связь, не путая все зависимости, созданные наЭто?".Это не весело.

0 голосов
/ 31 июля 2010

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

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

0 голосов
/ 29 июля 2010

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

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

select from order join customer using (customer_id)
where
    order.order_date between ? and ?
    and customer.name = ?

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

select from order join order_detail using (order_id) join customer using (customer_id)
where
    order.order_date between ? and ?
    and customer.name = ?

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

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

Суть: Нормальная форма и домен - это одно, но производительность часто требует компромиссов . Вы можете скрыть некоторые из них (закрыть разделение видом), но не все (дублировать / агрегировать поля для более быстрого выбора).

0 голосов
/ 29 июля 2010

Если вы хотите избежать дублирования данных, вам следует использовать решение с 2 или 3 таблицами.Например, если у вас есть столбцы External в таблице Order, значение может существовать несколько раз.Если данные выглядят так:

ID   ExternalCompanyName
1    ACME
2    ACME
3    My Company
4    ACME

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

Кажется, что это не отношение 1 к 1 между заказом икомпания / учетная запись, если каждая компания / учетная запись не может иметь только один заказ.это больше похоже на отношение 1-ко-многим.

Теперь, что произойдет, если при обновлении ExternalCompanyName в среде с одной таблицей будет допущена ошибка, и обновятся только некоторые строки.У вас есть несколько строк с ACME и несколько строк с ACME, Inc. В итоге вы столкнулись с ситуацией с неверными данными.

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

...