Как хранить исторические данные

162

Некоторые сотрудники и я начали дискуссию о том, как лучше хранить исторические данные. В настоящее время для некоторых систем я использую отдельную таблицу для хранения исторических данных и сохраняю исходную таблицу для текущей активной записи. Итак, допустим, у меня есть таблица FOO. В моей системе все активные записи будут отправляться в FOO, а все исторические записи - в FOO_Hist. Многие различные поля в FOO могут быть обновлены пользователем, поэтому я хочу вести точный отчет обо всем обновленном. FOO_Hist содержит те же поля, что и FOO, за исключением автоматически увеличивающегося HIST_ID. Каждый раз , когда FOO обновляется, я выполняю оператор вставки в FOO_Hist подобное: insert into FOO_HIST select * from FOO where id = @id.

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

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

Как вы или ваша компания справляетесь с этим?

Я использую MS SQL Server 2008, но я бы хотел, чтобы ответ был общим и произвольным для любой СУБД.

Аарон
источник

Ответы:

80

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

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

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

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

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

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

ConcernedOfTunbridgeWells
источник
Я думаю, я понимаю, что вы говорите. Так что я сделал с моей таблицей FOO_Hist действительно создание таблицы аудита. Вместо использования триггера для вставки в таблицу аудита при обновлении, я просто запустил оператор в программе. Это правильно?
Аарон
6
Довольно много. Однако лучше вести такого рода аудит с использованием триггеров; триггеры следят за тем, чтобы любые изменения (включая исправления данных вручную) регистрировались в журналах аудита. Если у вас есть более 10-20 таблиц для аудита, вероятно, быстрее всего будет создать инструмент генератора триггеров. Если трафик диска для журналов аудита является проблемой, вы можете поместить таблицы журнала аудита на отдельный набор дисков.
ConcernedOfTunbridgeWells
Да, я на 100% согласен с этим. Спасибо.
Аарон
40

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

Мы используем то, что называется моделью Master - Detail, которая в простейшем состоит из:

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

Таблица подробностей / истории, например, называется Widget_Detailsвызывающей и содержит как минимум:

  • ID - первичный ключ. Деталь / исторический идентификатор
  • MASTER_ID - например, в данном случае, называемый «WIDGET_ID», это FK для основной записи
  • START_DATETIME - отметка времени, указывающая начало этой строки базы данных
  • END_DATETIME - отметка времени, указывающая конец этой строки базы данных
  • STATUS_CONTROL - в одном столбце с символом указывается статус строки. «C» указывает текущее значение, NULL или «A» будет историческим / архивным. Мы используем это только потому, что не можем индексировать END_DATETIME как NULL
  • CREATED_BY_WUA_ID - хранит идентификатор учетной записи, которая вызвала создание строки
  • XMLDATA - хранит фактические данные

Таким образом, сущность начинается с того, что у 1 строки в главной и 1 строки в деталях. Деталь, имеющая конечную дату NULL и STATUS_CONTROL 'C'. Когда происходит обновление, текущая строка обновляется, чтобы иметь END_DATETIME текущего времени, а для status_control задано значение NULL (или «A», если желательно). В таблице сведений создается новая строка, которая по-прежнему связана с тем же мастером, с status_control 'C', идентификатором лица, выполняющего обновление, и новыми данными, хранящимися в столбце XMLDATA.

Это основа нашей исторической модели. Логика создания / обновления обрабатывается в пакете Oracle PL / SQL, поэтому вы просто передаете функции текущий идентификатор, ваш идентификатор пользователя и новые данные XML, а внутри все выполняет обновление / вставку строк, чтобы представить их в исторической модели. , Время начала и окончания указывают, когда эта строка в таблице активна.

Хранилище дешево, мы обычно НЕ УДАЛЯЕМ данные и предпочитаем вести контрольный журнал. Это позволяет нам видеть, как выглядят наши данные в любой момент времени. Индексируя status_control = 'C' или используя View, беспорядок не является проблемой. Очевидно, что ваши запросы должны учитывать, что вы всегда должны использовать текущую (NULL end_datetime и status_control = 'C') версию записи.

Крис Камерон-Миллс
источник
Привет Крис, если ты сделаешь это, ID (первичный ключ) должен быть изменен правильно? как насчет реляционной с другой таблицей, если она используется другими?
Projo
@projo ID в вашей главной таблице - это PK и концептуально «PK» для любой концепции, с которой вы имеете дело. Идентификатором в таблице сведений является PK, чтобы идентифицировать хронологическую версию для мастера (который является другим столбцом в детализации). При формировании отношений вы часто ссылаетесь на истинный PK вашей концепции (т. Е. Идентификатор вашей основной таблицы или столбец MASTER_ID в вашей детализации) и используете STATUS_CONTROL = 'C', чтобы убедиться, что вы получаете текущую версию. В качестве альтернативы вы можете ссылаться на идентификатор детали, чтобы связать что-то с конкретным моментом времени.
Крис Кэмерон-Миллс
+1 Я реализовал этот шаблон с большим успехом на нескольких крупных проектах.
Логика трех значений
Мы используем тот же подход. Но теперь мне интересно, лучше ли хранить только START_DATETIME и не хранить END_DATETIME
bat_ventzi
Пара вариаций в моем опыте. Если ваша сущность «закончилась», то есть заархивирована или удалена, то у вас фактически не может быть подробных записей с контролем состояния «С», то есть нет текущей строки, хотя вы не знаете, когда это произошло. В качестве альтернативы, вы можете установить end_datetime в последней строке, а наличие строки 'end' 'C' может указывать на то, что объект теперь удален / заархивирован. Наконец, вы можете представить это через другой столбец, СТАТУС, который, возможно, у вас уже есть.
Крис Кэмерон-Миллс
15

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

Если вы попробуете другой подход достаточно скоро, вы столкнетесь с проблемами:

  • накладные расходы на техническое обслуживание
  • больше флагов в выборе
  • замедление запросов
  • рост таблиц, индексов
Александр
источник
7

В SQL Server 2016 и более поздних версиях появилась новая функция, которая называется Temporal Tables и предназначена для решения этой проблемы с минимальными усилиями разработчика . Концепция временной таблицы похожа на сбор данных изменений (CDC), с той разницей, что временная таблица абстрагирует большинство вещей, которые вам приходилось делать вручную, если вы использовали CDC.

Вениамин
источник
2

Изменить сбор данных: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server?view=sql-server-2017

Он поддерживается в SQL Server 2008 R2, возможно, поддерживается в SQL Server 2008.

ребро
источник
Обратите внимание, что Change Data Capture предназначен только для краткого хранения истории данных. См. Временные таблицы SQL Server, сбор данных изменений и отслеживание изменений .
Эдвард Брей
1

Просто хотел добавить опцию, которую я начал использовать, потому что я использую Azure SQL, а использование нескольких таблиц было слишком громоздким для меня. Я добавил триггер вставки / обновления / удаления в свою таблицу, а затем преобразовал изменения до / после в json с помощью функции «FOR JSON AUTO».

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

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

Я узнал об этом по этой ссылке здесь

JakeHova
источник
0

Вы могли бы просто разделить таблицы нет?

Стратегии секционированных таблиц и индексов с использованием SQL Server 2008 Когда размер таблицы базы данных увеличивается до сотен гигабайт или более, загрузка новых данных, удаление старых данных и обслуживание индексов может стать более трудным. Просто размер таблицы приводит к тому, что такие операции занимают намного больше времени. Даже данные, которые должны быть загружены или удалены, могут быть очень большими, что делает операции INSERT и DELETE для таблицы нецелесообразными. Программное обеспечение базы данных Microsoft SQL Server 2008 обеспечивает разбиение таблиц, чтобы сделать такие операции более управляемыми ».

clyc
источник
Да, я могу разделить таблицы, но является ли это стандартом при работе с историческими данными? Следует ли включать исторические данные в ту же таблицу, что и активные данные? Вот вопросы, которые я хотел обсудить. Это также не является произвольным, поскольку это относится к SQL Server 2008.
Аарон,
0

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

HLGEM
источник
2
Труднее ли JOINдве таблицы в нескольких исторических отчетах или труднее изменить каждую вставку / обновление / удаление каждой таблицы, чтобы быть в курсе исторических проблем? На самом деле, журнал аудита будет включать даже текущие данные в таблицу истории, поэтому текущая таблица даже не требуется в отчете.
0

Другой вариант - архивировать оперативные данные [ежедневно | ежечасно | независимо]. Большинство механизмов баз данных поддерживают извлечение данных в архив .

По сути, идея состоит в том, чтобы создать запланированное задание Windows или CRON, которое

  1. определяет текущие таблицы в оперативной базе данных
  2. выбирает все данные из каждой таблицы в файл CSV или XML
  3. сжимает экспортированные данные в ZIP-файл, предпочтительно с отметкой времени создания в имени файла для упрощения архивирования.

Многие механизмы баз данных SQL поставляются с инструментом, который можно использовать для этой цели. Например, при использовании MySQL в Linux для задания извлечения можно использовать следующую команду в задании CRON:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz
Майкл
источник
2
Это совсем не подходит для исторических данных, потому что если кто-то изменит значение и вернет его обратно в течение цикла архивирования, обновления будут потеряны. Также нет простого способа посмотреть на изменения одного объекта с течением времени или частично восстановить его.
Sgoettschkes
0

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

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

Архив / История : для случаев, таких как отслеживание предыдущего адреса, номера телефона и т. Д., Лучше создать отдельную таблицу FOO_HIST, если ваша схема активной таблицы транзакций в будущем существенно не изменится (если ваша таблица истории должна иметь такую ​​же структуру). если вы ожидаете нормализацию таблицы, изменение типа данных, добавление / удаление столбцов, сохраняйте свои исторические данные в формате xml. определите таблицу со следующими столбцами (идентификатор, дата, версия схемы, XMLData). это легко справится с изменениями схемы. но вы должны иметь дело с XML, и это может привести к усложнению поиска данных.

Дэнни Д
источник
0

Вы можете создать материализованные / индексированные представления на таблице. На основании ваших требований вы можете сделать полное или частичное обновление просмотров. Пожалуйста, посмотрите это, чтобы создать mview и войти. Как создать материализованные представления в SQL Server?

Manu
источник