Должен ли я добавить этот новый столбец в таблицу клиентов или в отдельную новую таблицу? - PullRequest
17 голосов
/ 12 февраля 2010

У меня есть таблица клиентов с информацией о наших клиентах (идентификатор, логин, имя, информация о контактах, различные параметры, столбец TS и т. Д., ~ 15 столбцов, ~ несколько сотен клиентов).

Теперь нам нужно ежедневно рассылать обновления нашим крупнейшим клиентам (<10% всех клиентов). И мне нужно хранить метку времени последнего обновления, которое было отправлено клиенту, поэтому в следующий раз я буду отправлять только новые обновления (я имею в виду обновления в строках заказа с TS больше, чем хранимый TS). </p>

Если я добавлю новый столбец «LastUpdatesSentTS» в таблицу клиентов, то, как я понимаю, он будет соответствовать правилам нормализации (если нет, дайте ссылку, подтверждающую, что она нарушит нормализацию).

Но, как я также знаю, с физической точки зрения проектирования баз данных лучше создать новую таблицу с 2 столбцами [CustomerID, LastUpdatesSentTS], поскольку только у 10% клиентов эта информация TS будет храниться. Я имею в виду, что если я добавлю столбец в таблицу клиентов - у большинства клиентов в этом столбце будет нулевое значение. Кроме того, если я создаю отдельную новую таблицу, возможно, будет лучше удалить логический столбец «SendUpdates» из таблицы клиентов (поскольку я смогу понять, каким клиентам нужно отправлять обновления, присоединив таблицу клиентов к новой таблице). Кроме того, в этом случае я боюсь, что через несколько лет у меня будет куча очень маленьких таблиц, когда все это может быть в таблице клиентов (без нарушения нормализации, как я понимаю).

Проще говоря, я вижу 2 возможных варианта оформления стола:

1)

Table customers:  
[CustomerID, Name, ..., SendUpdates, LastUpdatesSentTS]

2)

Table customers:  
[CustomerID, Name, ...]  
Table customer_updates_sending:
[CustomerID, LastUpdatesSentTS]

Что вы думаете?

Ответы [ 7 ]

16 голосов
/ 12 февраля 2010

Я бы посоветовал вам сделать это в качестве второй отдельной таблицы.

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

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

В принципе, я мог бы спросить себя:

"Буду ли я в любой момент в будущем, нужно знать о клиенте история обновлений, а не только самое последнее? "

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

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

Даже если вас не интересует история обновлений клиентов, я лично предпочитаю подход с двумя таблицами, поскольку он, безусловно, позволяет вести исторический учет и предлагает более продуманный подход (так как только некоторые записи из таблицы клиентов будут нужны записи во 2-й таблице "обновлений"). Тем не менее, см. Мой РЕДАКТИРОВАТЬ ниже для получения дополнительной информации. Если бы я знал, что история никогда не потребуется для этих данных, я бы реализовал в качестве отдельного дополнительного поля в таблице существующего клиента.

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

EDIT:

(в ответ на комментарии к моему ответу).

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

В зависимости от характера «дополнительных» фрагментов данных, необходимо принять прагматичное решение относительно того, как они будут реализованы. Аарон предполагает, что в случае поля даты «LastUpdate» наличие большого числа NULL в 90% таблицы клиентов - неплохая вещь, и я согласен с ним здесь в этом, с точки зрения NULL х, это не плохо. Мое собственное предложение использовать подход с двумя таблицами было не столько основано на желании удалить NULL (хотя оно и делает это), но скорее на том, чтобы история"LastUpdate" даты могут быть сохранены.

Конечно, если ведение истории совершенно не требуется (и имейте в виду, что то, что не требуется сегодня, вполне может потребоваться завтра), то реализация этой даты «LastUpdate» в качестве дополнительного поля в той же таблице «Клиент» будет быть в порядке. На самом деле, если бы когда-либо существовала прямая взаимно-однозначная связь между одним клиентом и одной датой «последнего обновления», разбивать ее на 2 таблицы было бы неправильно. В этом случае я бы реализовал его как дополнительное поле в таблице клиентов, поскольку теперь это скалярное свойство этого клиента.

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

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

6 голосов
/ 12 февраля 2010

«Если сомневаешься, сделай самое простое, что могло бы сработать» - Уорд Каннингем

В некоторых случаях я бы сказал "добавить вторую таблицу", но в этом случае я не чувствую, что это оправдано. Насколько я понимаю, нет необходимости вести историю значений для этого атрибута. Стол маленький. И, в конечном счете, то, что у вас есть, является атрибутом клиента. Конечно, не все они будут заселены, но для меня это второстепенное соображение. Многие поля имеют значения NULL в большинстве случаев, но это не означает, что вы обязательно должны создать вторую таблицу для их хранения. Сохраняйте это как можно более простым (и максимально нормализованным), но не более простым (или нормальным - :-). Так что если бы это был я, я бы добавил эти поля в таблицу CUSTOMERS. YMMV.

Делись и наслаждайся.

3 голосов
/ 12 февраля 2010

Я бы пошел с вариантом 2.

Мне не нравятся такие столбцы, как SendUpdates. ИМО, лучше хранить это при наличии строки в другой таблице.

SELECT * FROM customer_updates_sending;

проще и быстрее, чем

SELECT * FROM  customers WHERE SendUpdates = 1;

Дальнейшие мысли в ответ на комментарий:

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

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

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

У вас не будет больших запросов с 15 объединениями, потому что вам нужно только объединение, которое связано с любой задачей, которую вы сейчас выполняете. Каждый раз, когда вы не отправляете письмо, эта информация вам не нужна. И когда вам нужна эта информация, это 1 присоединение.

1 голос
/ 12 февраля 2010

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

В целом, при рассмотрении этого типа вопросов одним из соображений, возможно, наиболее важным, является типичные схемы использования для различных частей данных .
Правдоподобным предположением является то, что таблица Customer интенсивно используется [главным образом] только для чтения; такое использование может поддерживаться путем отдельного хранения обновленных (и не часто запрашиваемых) данных, таких как отметка времени последнего уведомления. Если информация из связанной таблицы будет чаще включаться в запросы, то может быть целесообразно вместо нее поместить эту информацию в основную таблицу.

О проблемах с редким использованием столбца меток времени обновления
(безмолвие, так как подход с двумя таблицами, скорее всего, будет выбран, но в целом ...)
Тот факт, что только 10% записей будут содержать некоторую информацию в столбце метки времени, намекает на некоторую «расточительность», если бы мы выбрали вариант 1. На самом деле редкое использование этого столбца мало влияет на размер базы данных и производительность в целом. Например, если таблица легко включает столбец переменной длины, накладные расходы по размеру фактически равны нулю; если это первый обнуляемый столбец или столбец переменной длины, будет взиматься налог на минимальный размер, но он не должен иметь большого значения. (В более поздних версиях SQL Server 2005, я думаю, можно использовать разреженный столбец, хотя это вряд ли стоит для базы данных размером в тысячи или даже десятки тысяч записей.)

В столбце "sendudpate"
Также неплохо было бы удалить логический столбец sendupdate из главной таблицы, поместив всю связанную с обновлением информацию в связанную таблицу. Тем не менее, я полагаю, что тот факт, что клиент получает обновления, не должен подразумеваться для базового клиента, имеющего запись в соответствующей таблице. Вместо этого введите столбец «sendupdate» в соответствующий, возможно, не просто логический, а, например, код частоты (например, 0 = нет обновлений, 1 = обновляйте ежедневно, 7 = обновляйте еженедельно и т. Д.). Я предлагаю, чтобы все клиенты имели запись в соответствующей таблице, но тот факт, что они имеют такую ​​запись, является необходимым, но не достаточным условием, например, позволяет временно отключить обновления и т. Д. И т. Д.

О том, «ломается» ли подход одной таблицы и правила нормализации
Важно различать физический и логический дизайн базы данных. Вполне возможно иметь физическую схему, которая не нарушает никаких правил нормализации, но которая не является логически нормализованной. В общих чертах, одна основная мантра нормализации - хранить только один тип сущности на таблицу. Пока нет дублирования данных, можно поместить в одну и ту же таблицу в одной широкой записи информацию, фактически относящуюся к двум логическим объектам.
Иллюстрированный с базой данных клиентов в вопросе, можно решить, что физические записи будут включать дату последнего обновления, отправленного данному клиенту. Справедливо, физически правило нормализации не нарушено ... Однако логически можно утверждать, что «Административная информация клиента» (Имя, адрес ...) и «Информация обновления» (дата последнего отправленного обновления) - это две разные сущности, даже хотя они, кажется, находятся в отношении 1-1 на данный момент .
Следовательно, состояние нормализации логической модели, где сущности эффективно определены, часто находится в глазах наблюдателя, поскольку можно (иногда очень справедливо и разумно) утверждать, что элементы данных, которые все имеют 1-1 отношения принадлежат одному и тому же понятию (сущности).


Q: Что такое логическая и физическая нормализация? ... Разве не нормализуются правила нормализации? Я попытался объяснить это выше.
«Физическая нормализация» (или, скорее, нормальная форма физической схемы) рассматривает фактическую структуру таблиц и их взаимосвязей и применяет простые правила, чтобы узнать, какой нормальной форме удовлетворяет такая схема.
«логическая нормализация» (или, скорее, нормальная форма модели данных) рассматривает эффективные объекты, найденные в системе.
Таким образом, чтобы привести другой пример, при разработке простой базы данных о продаже домов для продажи можно решить, что единый концепт «ДОМА» будет храниться в одной таблице со столбцами, такими как «Адрес», «Кухня_Расположение», «Ливинг_Рум_Ареа» и т.д. И такая таблица будет «работать» и технически иметь определенную нормальную форму; это было бы несколько непрактично, не допуская перечисления домов с двумя кухнями и т. д. В качестве альтернативы можно было бы увидеть дом как «Местоположение» (адрес и, возможно, другую информацию администратора) и «Комнаты» (тип, поверхность, информация о настиле ...), где каждая концепция (местоположение, комната) хранится в отдельной таблице, причем одно местоположение связано с несколькими комнатами.
Таким образом, обе эти модели можно поместить в нормальную физическую схему, можно сказать, что первая модель денормализована (на логическом уровне) из-за того факта, что она не отражает должным образом действующие объекты.


Q: Не понимаю, как вы предлагаете явно отметить тот факт, что клиент получает обновления? A:

 SELECT whatever
 FROM Customers
 JOIN NotificationTable N on N.CustomerId = C.CustomerId
 WHERE N.notificationFrequency > 0

В приведенном выше,
- JOIN фиксирует первое условие для клиента, которого нужно уведомить: должна быть соответствующая запись в таблице уведомлений.
- предикат WHERE N.notificationFrequency> 0 фиксирует очень явное условие, чтобы столбец notificaionFrequency был положительным.

0 голосов
/ 27 февраля 2013

@ CraigTP, опять же, к вашему второму пункту тоже. Есть гораздо лучшие и более полные способы ведения истории записей. Что касается этого, это может быть сделано (и должно быть сделано) на полном столе клиента. Это в основном называется аудитом. У вас есть триггеры для этого. У вас есть довольно простые подходы в спящем режиме, чтобы сделать это из коробки для этого. Наконец, я хотел бы добавить новый столбец в существующий дизайн таблицы

РЕДАКТИРОВАТЬ Я вернулся к этому и думаю, что я тоже пропустил часть SendUpdate. ИМХО, что столбцы также не оправданы (в любой таблице как таковой). Если вы сохраните это, это переходная зависимость, которую вы должны нормализовать в 3NF. Но в любом случае я считаю, что не допуская историю и не допуская расширения, добавление нового столбца - это путь без столбца SendUpdate.

0 голосов
/ 12 февраля 2010

Я бы хотел иметь простой способ ежедневной записи о том, какие обновления я отправлял. Это не другая таблица данных о клиентах, а таблица дат, в которую каждому клиенту было отправлено обновление. Вопрос, который вам зададут: «Получил ли клиент« А »обновление в прошлый вторник?» Единственный ответ, который может дать решение за одним столом: «Я не знаю, но они получили его в пятницу». Может не быть приемлемым Без истории вы не сможете отправить обновление, которое клиент не получил.

0 голосов
/ 12 февраля 2010

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

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

Я бы предпочел вариант 2, так как он кажется мне более интуитивным.

...