Проектирование баз данных для изменений?

Как я объясняю в мой ответ на другой вопрос, PECS - это мнемоническое устройство, созданное Джошем Блохом, чтобы помочь вспомнить производителя extends, Consumer super.

Это означает, что когда параметризованный тип, передаваемый методу, будет выдавать экземпляры из T (они будут извлечены из него каким-либо образом), следует использовать ? extends T, поскольку любой экземпляр подкласса T также является T.

Когда параметризованный тип, передаваемый методу, будет потреблять экземпляры T (они будут переданы в он должен что-то сделать), ? super T следует использовать, потому что экземпляр T можно законно передать любому методу, который принимает некоторый супертип T. Например, Comparator можно использовать на Collection. ? extends T не будет работать, потому что Comparator не может работать на Collection.

blockquote>

Обратите внимание, что обычно вы должны использовать только ? extends T и ? super T для параметров какого-либо метода. Методы должны использовать T только как параметр типа для типичного типа возврата.

123
задан RAnders00 10 January 2018 в 17:31
поделиться

13 ответов

  1. Делают не , помещает все это в одну таблицу с атрибутом различителя IsCurrent. Это проблемы правых дел по линии, требует суррогатных ключей и всех видов других проблем.
  2. Дизайн 2 действительно имеет проблемы с изменениями схемы. При изменении Списка сотрудников, необходимо изменить таблицу EmployeeHistories и все связанные sprocs, которые идут с ним. Потенциально удваивает Вас усилие по изменению схемы.
  3. Дизайн 1 работает хорошо, и, если сделано правильно не стоит многого с точки зрения хита производительности. Вы могли использовать схему XML и даже индексируете для преобладания над возможными проблемами производительности. Ваш комментарий о парсинге xml действителен, но Вы могли легко создать представление с помощью xquery - который можно включать в запросы и присоединиться к. Что-то вроде этого...
CREATE VIEW EmployeeHistory
AS
, FirstName, , DepartmentId

SELECT EmployeeId, RevisionXML.value('(/employee/FirstName)[1]', 'varchar(50)') AS FirstName,

  RevisionXML.value('(/employee/LastName)[1]', 'varchar(100)') AS LastName,

  RevisionXML.value('(/employee/DepartmentId)[1]', 'integer') AS DepartmentId,

FROM EmployeeHistories 
38
ответ дан 24 November 2019 в 01:17
поделиться

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

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

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

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

1
ответ дан 24 November 2019 в 01:17
поделиться

Как насчет:

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

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

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

2
ответ дан 24 November 2019 в 01:17
поделиться

Это кажется, что Вы хотите отследить изменения в определенных объектах со временем, например, идентификатор 3, "боба", "123 главных улицы", затем другой идентификатор 3, "боб" "234 вяза Св.", и так далее, в сущности способность рвать история пересмотра, показывающая каждый адрес "боб", была в.

лучший способ сделать это должно иметь, "текущее" поле на каждой записи, и (вероятно) метка времени или FK на дату/расписание.

Вставляет, должны затем установить, "является текущим", и также сброс "является текущим" на предыдущем, "текущая" запись. Запросы должны указать, "является текущим", если Вы не хотите всю историю.

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

0
ответ дан 24 November 2019 в 01:17
поделиться

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

4
ответ дан 24 November 2019 в 01:17
поделиться

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

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

3
ответ дан 24 November 2019 в 01:17
поделиться

Ramesh, я был вовлечен в разработку системы на основе первого подхода.
оказалось, что, храня изменения, поскольку XML является ведущим к огромному росту базы данных и значительно замедляющимся вещам.
Мой подход должен был бы иметь одну таблицу на объект:

Employee (Id, Name, ... , IsActive)  

, где IsActive является знаком последней версии

, Если Вы хотите связать некоторую дополнительную информацию с изменениями, можно составить отдельную таблицу, содержащую ту информацию, и связать ее с таблицами объекта с помощью отношения PK\FK.

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

  • Простая структура базы данных
  • Никакие конфликты начиная с таблицы не становятся только добавлением
  • , можно откатывать к предыдущей версии путем простого изменения флага IsActive
  • Никакая потребность в соединениях для получения объектной истории

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

8
ответ дан 24 November 2019 в 01:17
поделиться

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

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

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

3
ответ дан 24 November 2019 в 01:17
поделиться

Мы реализовали решение, очень похожее на решение, которое предлагает Chris Roberts, и это работает вполне прилично на нас.

Единственная разница - то, что мы только храним новое значение. Старое значение, в конце концов, хранится в предыдущей строке истории

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[NewValue] [varchar](5000) NULL

, Позволяет, говорят, что у Вас есть таблица с 20 столбцами. Таким образом, только необходимо сохранить точный столбец, который изменился вместо того, чтобы иметь необходимость сохранить всю строку.

13
ответ дан 24 November 2019 в 01:17
поделиться

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

Редактирование

В эти Таблицы истории эссе, автор ( Kenneth Downs ), рекомендует поддержать таблицу истории по крайней мере семи столбцов:

  1. Метка времени изменения,
  2. Пользователь, который внес изменение,
  3. маркер А для идентификации записи, которая была изменена (откуда история сохраняется отдельно текущего состояния),
  4. , Ли изменение было вставкой, обновлением, или удаляют,
  5. старое значение,
  6. новое значение,
  7. дельта (для изменений в численных значениях).

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

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

19
ответ дан 24 November 2019 в 01:17
поделиться

Способ, которым я видел сделанный в прошлом, имеют

Employees (EmployeeId, DateModified, < Employee Fields > , boolean isCurrent );

, Вы никогда не "обновляете" на этой таблице (кроме изменить допустимый из isCurrent), просто вставляете новые строки. Для любого данного EmployeeId только 1 строка может иметь isCurrent == 1.

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

Этот метод в порядке, но можно закончить с некоторыми сложными запросами.

Лично, я довольно люблю Ваш Дизайн 2 способ сделать его, который является, как я сделал его в прошлом также. Его простое для понимания, простой реализовать и простой поддержать.

Это также создает очень мало служебное для базы данных и приложения, особенно при выполнении запросов чтения, который вероятен, что Вы будете делать 99% времени.

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

6
ответ дан 24 November 2019 в 01:17
поделиться

Я думаю, что ключевой вопрос спросить вот, 'Кто / Что будет использованием истории'?

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

Составляют таблицу под названием 'AuditTrail' или что-то, что имеет следующие поля...

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[OldValue] [varchar](5000) NULL,
[NewValue] [varchar](5000) NULL

можно затем добавить столбец 'LastUpdatedByUserID' ко всем столам, на которые нужно накрыть каждый раз, когда Вы делаете обновление / вставляет на таблице.

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

Мы используем поле RecordID для хранения значения поля ключа обновленной таблицы. Если это - объединенный ключ, мы просто делаем конкатенацию строк с '~' между полями.

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

Просто мысль!

51
ответ дан 24 November 2019 в 01:17
поделиться

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

// 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
ответ дан 24 November 2019 в 01:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: