2016-11-18 3 views
1

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

Ниже приведен пример текущей таблицы в вопросе:

Подача Таблица:

ID (int) 
Subject (text) 
Processed (bit) 
SubmissionDate (datetime) 
Submitted (bit) 
... 

Новые требования:

  1. Поданная могут быть помечены как действительный или недействительный

  2. Причина должна быть предоставлена, когда представление помечен как недействительным. (Таким образом, представление может иметь InvalidReason)

  3. Материалы могут быть связаны друг с другом таким образом, что: Multiple действительные Материалы могут быть установлены в качестве «замены» в течение недействительных Представление.

Так что я в настоящее время принято решение легко и просто добавили новые атрибуты непосредственно к представлению таблицы, так что это выглядит следующим образом:

NEW Представление таблицы:

ID (int) 
Subject (text) 
Processed (bit) 
SubmissionDate (datetime) 
Submitted (bit) 
... 
IsValid (bit) 
InvalidReason (text) 
ReplacedSubmissionID (int) 

Все работает отлично, но это выглядит немного странно:

  1. Имея InvalidReason в качестве столбца, который будет NULL для большинства представлений.
  2. Имея ЗамененныйСообщениеID в качестве столбца, который будет NULL для большинства представлений.
  3. Если я правильно понимаю норма, InvalidReason может быть транзитно зависимым от IsValid бит.

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

Является ли это одним столом дизайн в порядке? У кого-то есть лучшие альтернативные идеи?

ответ

2

ли или не вы должны иметь единый дизайн таблицы действительно зависит от 1) Как вы будете запрашивая данные 2) Сколько данных будет в конечном итоге потенциально NULL в результирующей таблице.

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

New table: InvalidSubmissionInfo 
Id (int) (of invalid submissions; will have FK contraint on Submission table) 
InvalidReason (string) 

Кроме того, если вы будете запрашивая замененные представления отдельно вы может потребоваться таблица для этих целей:

New table: ReplacementSubmissions 
Id (int) (of the replacement submissions; will have FK contraint on Submission table) 
ReplacedSubmissionId (int) (of what got replaced; will have FK constraint on submission table) 

Чтобы получить остальную информацию, вам все равно придется присоединиться к таблице представлений.

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

+0

Спасибо за объяснение и ответ! просто то, что я искал! – Michael

1

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

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

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

Например: - Получение представления, которые не были заменены>

Select * from tblSubmission where ReplacedSubmissionID = 0 
+0

Благодарим за ответ! – Michael

1

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

Вы вводе текста в себя о InvalidReason и IsValid. Однако, вы пропустили Представлено и Опубликовано.

Всякий раз, когда вы моделируете объект, который каким-то образом обрабатывается и проходит последовательные изменения состояния, эти состояния действительно должны быть помещены в отдельную таблицу. Любая информация, касающаяся даты изменения состояния, причины изменения, авторизации и т. Д., Будет иметь функциональную зависимость от состояния, а не от объекта в целом, поэтому попытка сделать государственную информацию частью кортежа объекта будет терпеть неудачу при 2nf.

Проблема, вызываемая этой проблемой, показана в вашем самом вопросе. Вы уже зарегистрировались Отправлено и Представлено в кортеж. Теперь у вас есть другое состояние, которое вы хотите добавить. Если вы нормализовали данные представления, вы могли бы просто добавить другое состояние и продолжить.

create table StateDefs(
    ID  int auto_generated primary key, 
    Name varchar(16) not null, -- 'Submitted', 'Processed', 'Rejected', etc. 
    ...  -- any other data concerning states 

); 

create table Submissions(
    ID  int auto_generated primary key, 
    Subject varchar(128) not null, 
    ...  -- other data 
); 

create table SubmissionStates(
    SubID int not null references Submissions(ID), 
    State int not null references StateDefs(ID), 
    When date not null, 
    Description varchar(128) 
); 

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

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

Но не так уж сложно:

select ss.* 
from SubmissionStates ss 
where ss.SubID = :SubID 
    and ss.When =(
     select Max(When) 
     from SubmissionStates 
     where SubID = ss.SubID 
      and When <= Today()); 

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

+0

Удивительный ответ! Это то, что мне определенно необходимо объяснить. Мне очень нравится, насколько чистым и нормализованным этот подход, но он немного связан с тем, что он делает получение текущего состояния (что-то, что было в основном автоматическим) более сложным, особенно при настройке ORM. – Michael

+0

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

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