Управление версиями базы данных - PullRequest
3 голосов
/ 07 декабря 2009

Я сделал несколько проектов (система CMS и EC), для которых требовалось, чтобы некоторые данные были версионированы.

Обычно я прихожу с такой схемой

+--------------+
+ foobar       +
+--------------+
+ foobar_id    +
+ version      +
+--------------+

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

т.е.:

SELECT * FROM foobar WHERE foobar_id = 2 and version = (SELECT MAX(version) FROM foobar f2 WHERE f2 = 2)

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

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

Спасибо

Ответы [ 7 ]

5 голосов
/ 07 декабря 2009

Я предпочитаю иметь исторические данные в другой таблице. Я бы сделал foobar_history или что-то подобное и сделал бы FK для foobar_id. Это избавит вас от необходимости использовать подзапрос все вместе. Это дает дополнительное преимущество, заключающееся в том, что ваша первичная таблица данных не загрязняется тоннами исторических данных, которые вы, вероятно, не хотите видеть в 99% случаев, когда вы обращаетесь к ним.

Вы, вероятно, захотите создать триггер для обновления этих данных, поскольку для этого потребуется скопировать текущие данные в _history, а затем выполнить обновление.

2 голосов
/ 07 декабря 2009

Раньше я работал в системе с историческими данными, и у нас было логическое значение, чтобы указать, какая из них была последней версией данных. Конечно, вам нужно поддерживать постоянство флага на аппликативном уровне. Затем вы можете создать индексы, которые используют флаг, и если вы предоставите его в предложении where, это быстро.

Pro:

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

Минусы:

  • необходимо поддерживать на аппликативном уровне

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

Pro:

  • чистое разделение истории от фактических данных
  • возможно каскадное удаление на уровне базы данных между фактическими данными и их историей в случае удаления объекта

Минусы:

  • нужно 2 запроса (или объединение), если вам нужна полная история (то есть старые данные + текущие данные)
  • строка, соответствующая последней версии данных, будет обновлена. Я слышал, что обновление выполняется медленнее, чем вставка, в зависимости от "размера" данных, которые изменились.

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

Подходит ли понятие истории в вашей доменной модели? Если нет, то у вас есть схема БД, которая отличается от вашей концептуальной модели предметной области. Если на уровне домена фактические данные и старые данные должны обрабатываться одинаково, наличие двух таблиц усложняет проект. Просто рассмотрите случай, когда вам нужно вернуть полную историю (старая + новая). Самым простым решением было бы иметь один класс для каждой таблицы, но тогда вы не можете вернуть список так же легко, как если бы у вас была только одна таблица. Но если это две разные концепции, то хорошо, чтобы история была первоклассной в вашем дизайне.

Я бы также рекомендовал эту статью М. Фаулера, которая также интересна, когда речь идет о временных данных: Шаблоны для вещей, которые меняются со временем

2 голосов
/ 07 декабря 2009

Самым чистым решением, на мой взгляд, было бы иметь таблицу History для каждой таблицы, для которой требуется версионирование. Другими словами, есть таблица foobar, а затем таблица foobar_History с триггером на foobar, который будет записывать существующие данные в таблицу History с отметкой времени и пользователем, который изменил данные. Более старые данные легко запрашиваются, сортируются по убыванию отметки времени, и вы знаете, что данные в основной таблице всегда имеют самую последнюю версию.

1 голос
/ 07 декабря 2009

Обычный метод - добавить столбец version_status для текущего / просроченного. Также обратите внимание, что если вы храните новые и старые записи в одной и той же таблице, у вас должен быть бизнес-ключ (натуральный) для вашей сущности, например, name + pin, поскольку первичный ключ будет меняться (увеличиваться) с каждой строкой. *

TABLE foobar(foobar_id PK, business_key, version, version_status, .....)

SELECT * 
FROM foobar 
WHERE business_key = 'myFoobar3' AND version_status = 'current'

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

1 голос
/ 07 декабря 2009

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

0 голосов
/ 07 декабря 2009

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

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

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

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

0 голосов
/ 07 декабря 2009

Если бы вы использовали Oracle, вы могли бы использовать аналитические функции

выберите * из ( Выберите.* , row_number () over (разделение по порядку foobar_id по версии desc) rn ОТ ФУБАРА А ГДЕ foobar_id = 2 ) где рН = 1

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