Как я объясняю в мой ответ на другой вопрос, PECS - это мнемоническое устройство, созданное Джошем Блохом, чтобы помочь вспомнить производителя extends
, Consumer super
.
Это означает, что когда параметризованный тип, передаваемый методу, будет выдавать экземпляры из
T
(они будут извлечены из него каким-либо образом), следует использовать? extends T
, поскольку любой экземпляр подклассаT
также являетсяT
.Когда параметризованный тип, передаваемый методу, будет потреблять экземпляры
blockquote>T
(они будут переданы в он должен что-то сделать),? super T
следует использовать, потому что экземплярT
можно законно передать любому методу, который принимает некоторый супертипT
. Например,Comparator
можно использовать наCollection
.? extends T
не будет работать, потому чтоComparator
не может работать наCollection
.Обратите внимание, что обычно вы должны использовать только
? extends T
и? super T
для параметров какого-либо метода. Методы должны использоватьT
только как параметр типа для типичного типа возврата.
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
У нас были подобные требования, и что мы нашли, был то, что часто времена, которые пользователь просто хочет к , видят , что было изменено, не обязательно откатывают любые изменения.
я не уверен, каков Ваш вариант использования, но что мы сделали, был, создают и таблица Audit, которая автоматически обновляется изменениями в предприятии, включая дружественное название любых ссылок внешнего ключа и перечислений.
Каждый раз, когда пользователь сохраняет их изменения, мы перезагружаем старый объект, выполняем сравнение, записываем изменения и сохраняем объект (все сделаны в транзакции единой базы данных в случае, если существуют любые проблемы).
Это, кажется, работает очень хорошо на наших пользователей и сохраняет нас головная боль наличия абсолютно отдельной контрольной таблицы с теми же полями как наше предприятие.
Как насчет:
, Вы делаете первичный ключ (EmployeeId, DateModified), и получить "текущую" запись (записи), Вы просто выбираете МАКСА (DateModified) для каждого employeeid. Хранение IsCurrent является очень плохой идеей, потому что, в первую очередь, он может быть вычислен, и во-вторых, для данных слишком легко выйти из синхронизации.
можно также сделать представление, которое перечисляет только последние записи, и главным образом используйте это при работе в приложении. Хорошая вещь об этом подходе состоит в том, что у Вас нет дубликатов данных, и Вы не должны собирать данные из двух различных мест (текущий в Сотрудниках и заархивированный в EmployeesHistory) для получения всей истории или отката, и т.д.).
Это кажется, что Вы хотите отследить изменения в определенных объектах со временем, например, идентификатор 3, "боба", "123 главных улицы", затем другой идентификатор 3, "боб" "234 вяза Св.", и так далее, в сущности способность рвать история пересмотра, показывающая каждый адрес "боб", была в.
лучший способ сделать это должно иметь, "текущее" поле на каждой записи, и (вероятно) метка времени или FK на дату/расписание.
Вставляет, должны затем установить, "является текущим", и также сброс "является текущим" на предыдущем, "текущая" запись. Запросы должны указать, "является текущим", если Вы не хотите всю историю.
существуют дальнейшие тонкие настройки к этому, если это - очень большая таблица, или большое количество изменений ожидается, но это - довольно стандартный подход.
Изменения данных являются аспектом' допустимо-разовый ' понятие Временной Базы данных. Много исследования вошло в это, и появились много шаблонов и инструкций. Я записал долгий ответ с набором ссылок на этот вопрос для заинтересованных.
Если бы действительно журнал аудита - все, в чем Вы нуждаетесь, я склонился бы к контрольному решению для таблицы (вместе с денормализованными копиями важного столбца на других таблицах, например, UserName
). Следует иметь в виду, тем не менее, что горький опыт указывает, что единственная контрольная таблица будет огромным узким местом в будущем; это, вероятно, стоит усилия составить отдельные контрольные таблицы для всех Ваших контролируемых таблиц.
, Если необходимо отследить фактическое историческое (и/или будущее) версии, затем стандартное решение состоит в том, чтобы отследить тот же объект с несколькими строками с помощью некоторой комбинации запуска, конца и значений продолжительности. Можно использовать представление для создания текущих значений доступа удобными. Если это - подход, Вы берете, можно столкнуться с проблемами если имеющие версию ссылки на данные изменяемые но неимеющие версию данные.
Ramesh, я был вовлечен в разработку системы на основе первого подхода.
оказалось, что, храня изменения, поскольку XML является ведущим к огромному росту базы данных и значительно замедляющимся вещам.
Мой подход должен был бы иметь одну таблицу на объект:
Employee (Id, Name, ... , IsActive)
, где IsActive является знаком последней версии
, Если Вы хотите связать некоторую дополнительную информацию с изменениями, можно составить отдельную таблицу, содержащую ту информацию, и связать ее с таблицами объекта с помощью отношения PK\FK.
Этот путь можно сохранить всю версию сотрудников в одной таблице. Профессионалы этого подхода:
Примечание, что необходимо позволить первичному ключу быть не уникальным.
Если Вы хотите сделать первый, Вы могли бы хотеть использовать XML для Списка сотрудников также. Большинство более новых баз данных позволяет Вам запрашивать в поля XML, таким образом, это - не всегда проблема. И могло бы быть более просто иметь один способ получить доступ к данным сотрудника независимо, если это - последняя версия или более ранняя версия.
я попробовал бы второй подход все же. Вы могли упростить это при наличии всего одного Списка сотрудников с полем DateModified. EmployeeId + DateModified был бы первичным ключом, и можно сохранить новый пересмотр, просто добавив строку. Этот способ архивировать более старые версии и восстановить версии из архива легче также.
Другой способ сделать это могло быть datavault модель Dan Linstedt. Я сделал проект для голландского бюро статистики, которое использовало эту модель, и это работает вполне хорошо. Но я не думаю, что это непосредственно полезно для повседневного использования базы данных. Вы могли бы получить некоторое представление от того, чтобы читать его газеты все же.
Мы реализовали решение, очень похожее на решение, которое предлагает 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 столбцами. Таким образом, только необходимо сохранить точный столбец, который изменился вместо того, чтобы иметь необходимость сохранить всю строку.
статья History Tables в Программист баз данных блог мог бы быть полезным - отвечает на некоторые вопросы, повышенные здесь, и обсуждает устройство хранения данных дельт.
Редактирование
В эти Таблицы истории эссе, автор ( Kenneth Downs ), рекомендует поддержать таблицу истории по крайней мере семи столбцов:
Столбцы, которые никогда не изменяются, или чья история не требуется, не должны быть прослежены в таблице истории для предотвращения чрезмерного увеличения размера. Хранение дельты для численных значений может сделать последующие запросы легче, даже при том, что это может быть получено из старых и новых значений.
таблица истории должна быть безопасной, с несистемными пользователями, которым препятствуют вставить, обновив или удалив строки. Только периодическая чистка должна поддерживаться для сокращения полного размера (и, если разрешено вариантом использования).
Способ, которым я видел сделанный в прошлом, имеют
Employees (EmployeeId, DateModified, < Employee Fields > , boolean isCurrent );
, Вы никогда не "обновляете" на этой таблице (кроме изменить допустимый из isCurrent), просто вставляете новые строки. Для любого данного EmployeeId только 1 строка может иметь isCurrent == 1.
сложность поддержания это может быть скрыто представлениями и "вместо" триггеров (в оракуле, я считаю подобные вещи другим RDBMS), можно даже перейти к осуществленным представлениям, если таблицы являются слишком большими и не могут быть обработаны индексами).
Этот метод в порядке, но можно закончить с некоторыми сложными запросами.
Лично, я довольно люблю Ваш Дизайн 2 способ сделать его, который является, как я сделал его в прошлом также. Его простое для понимания, простой реализовать и простой поддержать.
Это также создает очень мало служебное для базы данных и приложения, особенно при выполнении запросов чтения, который вероятен, что Вы будете делать 99% времени.
к автоматическому создание таблиц истории и триггеров также было бы довольно легко поддержать (предположение, что это будет сделано через триггеры).
Я думаю, что ключевой вопрос спросить вот, 'Кто / Что будет использованием истории'?
, Если это будет главным образом для создания отчетов / человекочитаемая история, мы реализовали эту схему в прошлом...
Составляют таблицу под названием '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 для автоматической записи триггеров на основе определений таблицы.
Просто мысль!
Если вы хотите полагаться на данные истории (для отчетов), вы должны использовать такую структуру:
// 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)"