База данных - управление версиями данных

Я прочитал несколько вопросов по SO (например, этот) относительно управления версиями вашего данные в базе данных.

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

Я не прошу реального решения в синтаксисе SQL. В конце концов, я смогу понять это для себя (или опубликовать ТАК, когда придет время). Я просто прошу людей прокомментировать, как они будут это делать, и какие потенциальные проблемы с производительностью могли бы возникнуть, если бы я «пересмотрел» сотни миллионов записей. Или любые другие предложения, если они основаны на примере ниже.

Рассмотрим простой пример:

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL

Audit
------------------------------------------------
ID                UINT NOT NULL,
UserID            UINT NOT NULL,               -- Who
TableName         VARCHAR(50) NOT NULL,        -- What
OldRecID          UINT NOT NULL,               -- Where
NewRecID          UINT NOT NULL,
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

Я не уверен, как связать таблицу аудита с любыми другими таблицами (например, Person), если TableName является строкой?

Кроме того, если у меня есть три графических интерфейса для заполнения:

  1. Полная запись для идентификатора конкретного человека
  2. Табличное представление со списком всех лиц (по идентификатору)
  3. Представление, показывающее каждого человека с информацией о его ревизии под каждой записью (количество ревизий на человека, даты ревизий, комментарии к ревизиям и т. Д.), Упорядоченные по самым последним ревизиям.

Чтобы выполнить 1 и 2, что лучше: запросить таблицу Person или таблицу аудита?

Чтобы выполнить 3, мог бы так называемый эксперт по базам данных просто получить все записи и передать их программному обеспечению для обработки или сгруппировать их по идентификатору пользователя и дате воздействия? Обычно это обрабатывается одним или несколькими запросами?


person Jeach    schedule 17.04.2009    source источник
comment
Комментарий аудита вряд ли будет широко использоваться, если вы не заполните его автоматически.   -  person Jonathan Leffler    schedule 18.04.2009
comment
Это плохой вопрос, потому что он основан на мнении? Просто вау.   -  person Rei Miyasaka    schedule 01.05.2020


Ответы (4)


Я использовал различные схемы аудита на протяжении многих лет, и в настоящее время я собираюсь реализовать что-то вроде этого:

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL


Person_History
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL
AuditID           UINT NOT NULL


Audit
------------------------------------------------
ID                UINT NOT NULL,
UserID            UINT NOT NULL,               -- Who
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

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

Идентификатор аудита находится в таблицах * _History, поэтому при желании вы можете связать несколько изменений записей с одной записью аудита.

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

person DJ.    schedule 17.04.2009
comment
Спасибо за ответ! Так я и представлял себе сначала, но, поскольку мне нужно пересмотреть более 100 таблиц, я хотел избежать 100 таблиц ревизий и 100 таблиц аудита. Вот почему я пытался использовать таблицу аудита для всех своих таблиц, а также избегать дублирования каждой таблицы. Может быть, моя цель необоснованна, но именно поэтому я исследую и исследую это. Еще раз спасибо! - person Jeach; 18.04.2009
comment
+1 для таблицы истории за базовую таблицу. Часто возникает желание иметь одну таблицу аудита. Это может привести к серьезным проблемам с производительностью, если система довольно загружена. Таблица аудита становится узким местом для каждой транзакции. Проще говоря, если транзакция №2 ожидает завершения транзакции №1, ваш пользователь будет испытывать замедление. - person Karl; 18.04.2009
comment
Вам нужна только одна таблица аудита, но вам все равно нужна таблица истории для каждой из ваших основных таблиц. - person DJ.; 18.04.2009
comment
В моем случае таблица аудита - это просто дата / время и информация о пользователе. - person DJ.; 18.04.2009
comment
Замечательный момент, Карл ... именно такую ​​информацию я искал. Я планировал более широко использовать транзакции, что, как вы упомянули, может привести к проблемам с узкими местами. Я должен принять это во внимание. Возможно даже запустить тесты, чтобы оценить мое использование в реальном мире. Я готов смириться с большей сложностью схемы, но не хочу жертвовать хоть малейшей производительностью. Спасибо! - person Jeach; 18.04.2009
comment
если человек удален. будет ли запись удалена из таблицы person и таблицы person_history? что следует использовать в качестве указания в таблице аудита для удаленной записи? - person cometta; 02.06.2010
comment
Да, запись удаляется из таблицы людей, но перемещается в таблицу истории. Я предполагаю, что в таблице истории может быть индикатор, указывающий, что это удаленная запись. - person DJ.; 04.06.2010
comment
@Jeach Вы можете добавить столбец статуса и создать представление, которое показывает только текущие записи, и в любое время вы можете запрашивать представление. - person Durai Amuthan.H; 25.12.2013
comment
@DJ. Почему бы вам не добавить эти столбцы аудита в каждую таблицу? - person Jowen; 25.03.2015
comment
Хотя этот вопрос и ответ датируются 2009 годом, ознакомьтесь с темпоральными базами данных и их битемпоральными таблицами (стандарт SQL: 2011). - person VH-NZZ; 27.09.2015

Как насчет того, чтобы создать таблицу как обычно, иметь столбец ModifiedDate для каждой записи (и ModifiedBy, если хотите) и обеспечить доступ ко всем данным через материализованное представление, которое группирует данные по идентификатору, а затем выполняет HAVING ModifiedDate = MAX (ModifiedDate )?

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

Я всегда считал, что поддерживать разные таблицы с одним и тем же Colm сложно и подвержено ошибкам.

edit: Я только что вернулся к этому ответу через 12 лет после того, как написал его. Я бы сказал, что исходный вопрос неверен - вы должны проверять события на уровне пользователя, а не изменения столбцов базы данных.

person mcintyre321    schedule 18.04.2009
comment
Я экспериментировал с тем, что вы предлагали, используя MySQL. Я знаю, что это выполнимо (теоретически и кажется довольно эффективным). Проблема в том, что MySQL (я пробовал несколько версий) имеет серьезные ошибки в представлениях (subquries, сортировка, порядок и т. Д.). Это большая проблема в @ $$. - person Jeach; 25.01.2012
comment
Какова ваша стратегия кластерного индекса в таблице? - person JoeBrockhaus; 14.11.2014
comment
Не знаю, этот подход был просто мысленным экспериментом! - person mcintyre321; 17.11.2014

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

Я просто хотел задокументировать то, что нашел:

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

Но, к своему удивлению, я понял, что вы можете выполнять первые две транзакции, используя один оператор SQL, используя синтаксис SELECT INTO. Бьюсь об заклад, при этом производительность будет в сотни раз выше.

Тогда это оставит нас просто ОБНОВИТЬ запись новыми данными в базовой таблице.

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

person Jeach    schedule 18.04.2009
comment
Вы не найдете ни одной инструкции, чтобы сделать все сразу. Вы можете смоделировать сразу три с помощью транзакции или точки сохранения. - person Jonathan Leffler; 18.04.2009
comment
Был бы очень признателен пример Sql - person Jowen; 25.03.2015

Мне нравится ваша таблица аудита, это хорошее начало. У вас проблема с количеством элементов в вашей таблице аудита, поэтому я бы разделил ее на две таблицы:

Person
------------------------------------------------
ID                UINT NOT NULL,
PersonID          UINT NOT NULL,
Name              VARCHAR(200) NOT NULL,
DOB               DATE NOT NULL,
Email             VARCHAR(100) NOT NULL,
AuditID           UINT NOT NULL 

Audit
------------------------------------------------
ID                UINT NOT NULL,
TableName         VARCHAR(50) NOT NULL,        -- What
TableKey          UINT NOT NULL,
CreateDate        DATETIME NOT NULL  DEFAULT(NOW),
CreateUserID      UINT NOT NULL,
ChangeDate        DATETIME NOT NULL  DEFAULT(NOW),
ChangeUserID      UINT NOT NULL

Audit_Item
------------------------------------------------
ID                UINT NOT NULL,
AuditID           UINT NOT NULL,               -- Which audit record
UserID            UINT NOT NULL,               -- Who
OldRecID          UINT NOT NULL,               -- Where
NewRecID          UINT NOT NULL,
AffectedOn        DATE NOT NULL,               -- When
Comment           VARCHAR(500) NOT NULL        -- Why

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

  • Какие записи в вашей таблице людей являются текущими «настоящими» записями?
  • Как вы представляете всю историю изменений в записи Person? Если вы указываете на две записи в таблице Person, см. Пункт №1: какая из них является текущей записью?
  • Поля Create *, Change * сводятся из коллекции записей Audit_Item. Они есть только для удобства доступа.
  • Ключ AuditID в таблице Person позволяет вам вернуться к таблице аудита и получить доступ к истории отдельного человека без необходимости запрашивать таблицу аудита с предложением WHERE TableName='Person'
person Jeff Fritz    schedule 18.04.2009
comment
Я какое-то время смотрел на вашу «кардинальную» проблему и, кажется, просто не могу понять ваши предполагаемые выгоды? Разве UserID, CreateUserID и ChangeUserID не являются дубликатами одной и той же информации? Разве AffectedOn, CreateDate и ChangeDate тоже не дублируются? Алос, разве это не добавление новой транзакции SQL каждый раз, когда человек редактируется? Можете ли вы отредактировать свой пост, чтобы объяснить предлагаемое улучшение ... спасибо! - person Jeach; 20.04.2009
comment
Я думаю, вы неправильно поняли процесс принятого ответа. В таблице Person всегда будет только одна запись Person. Будет несколько строк Person_History. Audit указывает на несколько Person_History, но все они указывают на одного человека. Person является «текущим», каждый Person_History является предыдущей ревизией. - person JoeBrockhaus; 14.11.2014