Дизайн базы данных для ревизий? - PullRequest
117 голосов
/ 02 сентября 2008

В проекте есть требование хранить все ревизии (История изменений) для сущностей в базе данных. В настоящее время у нас есть 2 разработанных предложения для этого:

например. для субъекта "Сотрудник"

Дизайн 1:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- Holds the Employee Revisions in Xml. The RevisionXML will contain
-- all data of that particular EmployeeId
"EmployeeHistories (EmployeeId, DateModified, RevisionXML)"

Дизайн 2:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- In this approach we have basically duplicated all the fields on Employees 
-- in the EmployeeHistories and storing the revision data.
"EmployeeHistories (EmployeeId, RevisionId, DateModified, FirstName, 
      LastName, DepartmentId, .., ..)"

Есть ли другой способ сделать это?

Проблема с «Design 1» заключается в том, что нам приходится анализировать XML каждый раз, когда вам нужен доступ к данным. Это замедлит процесс и также добавит некоторые ограничения, например, мы не можем добавлять объединения в поля данных ревизий.

И проблема с «Проектом 2» заключается в том, что мы должны дублировать каждое поле во всех сущностях (у нас есть около 70-80 сущностей, для которых мы хотим сохранить исправления).

Ответы [ 16 ]

3 голосов
/ 02 сентября 2008

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

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

3 голосов
/ 02 сентября 2008

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

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

Другим способом сделать это может быть модель datavault Дэна Линстедта. Я сделал проект для голландского бюро статистики, которое использовало эту модель, и она работает довольно хорошо. Но я не думаю, что это непосредственно полезно для повседневного использования базы данных. Вы можете почерпнуть некоторые идеи, прочитав его статьи.

2 голосов
/ 25 июня 2009

Если вы хотите полагаться на исторические данные (по причинам отчетности), вы должны использовать структуру примерно так:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds the Employee revisions in rows.
"EmployeeHistories (HistoryId, EmployeeId, DateModified, OldValue, NewValue, FieldName)"

Или глобальное решение для приложения:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, OldValue, NewValue, FieldName)"

Вы можете сохранить свои ревизии также в формате XML, тогда у вас будет только одна запись для одной ревизии. Это будет выглядеть так:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, XMLChanges)"
2 голосов
/ 18 октября 2008

Как насчет:

  • EmployeeID
  • DateModified
    • и / или номер редакции, в зависимости от того, как вы хотите его отслеживать
  • ModifiedByUSerId
    • плюс любая другая информация, которую вы хотите отслеживать
  • Поля сотрудников

Вы делаете первичный ключ (EmployeeId, DateModified), и для получения «текущих» записей вы просто выбираете MAX (DateModified) для каждого employeeid. Хранение IsCurrent - очень плохая идея, потому что, во-первых, его можно вычислить, а во-вторых, слишком просто синхронизировать данные.

Вы также можете создать представление, в котором перечислены только самые последние записи, и в основном использовать его при работе в приложении. Хорошая вещь в этом подходе состоит в том, что у вас нет дубликатов данных, и вам не нужно собирать данные из двух разных мест (текущих в Employees и заархивированных в EmployeesHistory), чтобы получить всю историю или откат и т. Д.) .

1 голос
/ 02 сентября 2008

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

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

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

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

0 голосов
/ 02 сентября 2008

Звучит так, как будто вы хотите отслеживать изменения конкретных объектов с течением времени, например, ID 3, «bob», «123 main street», затем другой ID 3, «bob», «234 elm st» и т. Д., По сути, возможность извлечь историю изменений, показывающую, что каждый адрес «bob» был .

Лучший способ сделать это - иметь поле «is current» в каждой записи и (возможно) метку времени или FK для таблицы даты / времени.

Вставки должны затем установить «текущий», а также сбросить «текущий» на предыдущей «текущей» записи. Запросы должны указывать «is current», если вы не хотите всю историю.

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

...