Расширение Qbim, пример реализации битемпоральной модели данных
Примеры:
Предпосылки
Модель представления данных близка к шестой нормальной форме, но без избыточной нормализации.
Шестая нормальная форма введена Кристофером Дейтом как обобщение пятой нормальной формы для хронологической базы данных в своей книге Введение в системы баз данных.
Расширение Qbim представляет собой конструктор для разработки ERM на темпоральной основе, то есть, моделируемые объекты представляются временными срезами, последовательностями своих состояний во времени. Основное внимание и акцент в данном расширении сделан не на "декомпозиции до конца", а на поддержании темпоральности данных. Расширение реализует краткий набор логики хранения (создания, изменения) объектов в реляционной СУБД (на примере qhb), реализованный в слое хранимых процедур.
Модель данных
В рамках расширения Qbim рассматривается пример реализации модели хранения данных с указанием интервалов времени их действия. В отличие от нехронологических баз данных, хранящих только текущие данные, такая модель предполагает хранение данных, относящихся к прошлым, а также будущим периодам времени.
В предлагаемой реализации наряду с идентификатором кортежа данных указывается еще и интервал времени, в течение которого эти данные считаются актуальными. Эти интервалы могут относиться или пересекаться с настоящим временем, находиться в прошлом или описывать будущие значения. Интервалы хранятся в двух специально выделенных для этого полях типа timestamp, образующих полузакрытый интервал диапазонного типа tsrange.
Серия данных с одним идентификатором (далее объект), может образовывать непересекающиеся отрезки, или «версии», на временной оси «действия» этих данных (business time).
Дополнительная временная ось (system time) фиксирует, когда эти версии были созданы или изменены.
Реализация модели
Терминология
Термин | Определение |
---|---|
Объект | Некоторая оцифрованная, смоделированная из реального мира сущность. Обладает свойствами (атрибутами) и операциями над ними (методами). В определённые временные интервалы имеет фиксированный набор атрибутов. Но во времени, от интервала к интервалу, может менять набор атрибутов. В базе данных набор Объектов представляется сущностью/таблицей, его атрибуты представлены полями таблицы, методы реализованы хранимыми процедурами и функциями. |
Реализация объекта | Один из представителей объекта, выделяемый среди других с помощью идентификатора. В базе данных представляет собой серию записей одной таблицы/сущности. |
Версия реализации объекта | Один из наборов атрибутов объекта, постоянный в течении определенного временного интервала. В базе данных - одна из записей таблицы/сущности, принадлежащая объекту (по идентификатору), с определенным интервалом действия. |
Идентификатор реализации объекта | Идентификатор (id - обычно целое число, но может быть UUID-ом), однозначно определяющий реализацию объекта среди других объектов сущности. Все версии одного и того же объекта имеют один и тот же идентификатор. |
Идентификатор версии реализации объекта | Кортеж из идентификатора и интервала действия версии ( <id[, sd, ed]> ), однозначно определяющий объект и версию. |
Ограничения и умолчания
Зарезервированные поля
Для идентификатора и границ интервала действия в версионированных таблицах резервируются три поля:
Резервируемое поле | Тип | Назначение |
---|---|---|
id | bigint или uuid | идентификатор |
sd | timestamp | start date, начало периода действия |
ed | timestamp | end date, окончание периода действия |
Далее для bigint может использоваться термин «локальный идентификатор», а для uuid — «глобальный идентификатор».
Любой версионированный объект представляется набором своих версий реализации, связанных уникальным идентификатором реализации
(id) и набором атрибутов / свойств.
Каждая его версия - кортежем id и периодом действия этой версии (период представляется интервалом [sd, ed)
- включается
левая граница и не включается правая, типы границ - timestamp).
Первичный ключ версии объекта - составной: <id,[sd,ed)>
.
Версии не пересекаются и не содержат разрывов
Интервалы действия версий образуются как tsrange(sd,ed,'[)')
— полуоткрытый
интервал. Отсутствие пересечений интервалов реализуется ограничением вида:
constraint ... exclude using gist (id with =, tsrange(sd, ed) with &&) where ((sd <> ed))
Следует заметить, что при таких условиях допустимыми являются версии, в которых sd == ed
.
Это допущение сделано намеренно, поскольку:
- такие версии имеют нулевую длину, не пересекаются с другими версиями, не влияют на многие сценарии выбора версий и серий версий (непустых);
- при наличии фиксации времени изменения (system time) не пропадает и не требует изменения привязка к версии события (поле action.link_dt), поскольку привязка осуществляется именно к значению поля sd версионированной таблицы.
Описание версионируемых таблиц
Все версионируемые таблицы и их поля описываются в специально выделяемых таблицах obj и obj_item.
Таблица obj:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
tab | text | Название таблицы, где хранится объект. Если требуется, вместе с именем схемы |
dsc | text | Описание |
parent | bigint | Ссылка на ID верхнего в иерархии объекта |
is_temporal | bool | Признак темпоральности объекта |
is_logging | bool | Признак логирования действий пользователя для объекта |
is_global | bool | Признак глобального идентификатора у объекта. Для глобальных идентификаторов используется тип UUID, для локальных — bigint |
Таблица obj_item:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
up | bigint | Ссылка на объект (obj.id) |
ord | bigint | Порядковый номер атрибута |
name | text | Имя |
r_cls | bigint | Если имеется словарное поле, номер словаря (@CLS.ID) |
r_obj | bigint | Если имеется ссылка на объект, номер объекта |
option | jsonb | Дополнительные параметры. Например, указание о замене типа на массив для методов |
dsc | text | Описание атрибута |
Таблица action:
Поле | Тип | Назначение |
---|---|---|
id | bigserial | Идентификатор |
r_object | uref | Ссылка на изменяемый объект и его идентификатор |
lnk_dt | timestamp | Дата привязки к версии реализации события (SD) |
fct_dt | timestamp | Реальная дата версии (по началу транзакции) |
action | bigint | Произведенное действие. Словарь |
database | text | База данных, где расположен измененный объект |
schema | text | Схема, где расположен измененный объект |
dbuser | text | Пользователь базы данных, совершивший действие |
pid | text | Идентификатор процесса (Process ID), в рамках работы которого произведено изменение |
userid | uuid | UUID пользователя, вошедшего в систему и совершившего действие |
В таблицу action, если указано в obj.is_logging, записываются основания действий с объектами.
«Действие» (поле action) имеет следующую кодировку:
Код | Действие |
---|---|
1 | Создание |
2 | Изменение (при создании новой версии) |
3 | Изменение (при сокращении периода действия) |
4 | Закрытие версии |
ВАЖНО!
Для функционирования приложения достаточно важно, чтобы записи в эту таблицу записывались максимально быстро. Для этого есть возможность создавать таблицу как unlogged, а при включенном TARQ — также ... using append_only.
Методы работы с объектами
Для создания, изменения и закрытия объектов предлагаются методы, которые корректно отслеживают ограничения модели.
Методы выборки оставляются на выбор пользователя или приложения. Возможно выбрать
конкретную версию, действующую в указанный момент времени, или серии версий за
указанный период вместе со схлопнувшимися версиями (промежуточные версии с нулевой
длительностью, sd == ed
) или без них. Также возможны различные выборки совместно со
связанными
объектами.
Методы создания объекта
Для создания объекта (начальной версии объекта) предоставляются методы qbim_create()
:
CREATE FUNCTION qbim_create(
obj_id int8, tab text,
id int8,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','create_local_reg' language c volatile security definer;
CREATE FUNCTION qbim_create(
obj_id int8, tab text,
id uuid,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns uuid as 'MODULE_PATHNAME','create_global_reg' language c volatile security definer;
В одном из первых двух параметрах метода указывается идентификатор создаваемого объекта: по идентификатору в таблице obj описателя или по имени объекта (таблицы); возможно указание и идентификатора, и имени, но они должны соответствовать описателю.
В зависимости от типа идентификатора, используемого в объекте, третий параметр может
иметь тип bigint или uuid; его пропуск (указание null::bigint
или null::uuid
)
требует автоматической генерации идентификатора. В случае bigint значение может
выбираться из соответствующей последовательности, а в случае uuid — генерируется
случайное значение uuid.
Следующая пара параметров задает интервал действия версии, при пропуске (указании
null:timestamp
) используются значения по умолчанию, указанные при создании таблицы.
Как правило, для sd это текущее время (CURRENT_TIMESTAMP), а для ed —
«бесконечность» (infintity::timestamp
).
В параметре userid можно указать идентификатор конечного пользователя,
производящего операцию создания объекта, если таковой используется в приложении.
Если не используется, можно пропустить (указать null::uuid
).
Далее в переменном списке параметров (variadic "any") следует передавать параметры парами — имя и значение, или не передавать вовсе. В любом случае количество переменных должно быть четным. Атрибуты объекта, пропущенные в этом списке, получат значения NULL или будут установлены в соответствии с умолчаниями, заданными при создании таблицы.
При создании объекта, если это указано в obj, будет зафиксировано действие в таблице action.
Методы изменения объекта (создания версии)
Для изменения объекта предоставляются методы qbim_change()
. Создание версии
возможно в любой период, пересекающий периоды имеющихся у объекта версий (не должно
получаться разрывов).
Параметры аналогичны методам qbim_create()
. Умолчания для sd и ed — текущее
время и «бесконечность» соответственно.
Новая версия может дополнять (с любой стороны) имеющиеся, пересекать любое количество имеющихся версий, покрывать полностью или частично уже имеющиеся версии.
В случае, если в переменном списке параметров опущено какое-то из полей, оно будет подставлено из соответствующей более ранней версии объекта, подходящей по времени.
Если в результате изменения какие-то из имеющихся версий будут полностью перекрыты
новой версией, они будут считаться схлопнувшимися (sd == ed
).
Методы qbim_change()
:
CREATE FUNCTION qbim_change(
obj_id int8, tab text,
id int8,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_local_reg' language c volatile security definer;
CREATE FUNCTION qbim_change(
obj_id int8, tab text,
id uuid,
sd timestamp, ed timestamp,
userid uuid,
variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_global_reg' language c volatile security definer;
При изменении объекта, если это указано в obj, будет зафиксировано действие в таблице action.
Методы закрытия объекта (версии)
Пара методов, закрывающих версии объекта текущей или указанной датой:
CREATE FUNCTION qbim_close(
obj_id int8, tab text,
id int8,
ed timestamp,
userid uuid)
returns int8 as 'MODULE_PATHNAME','close_local' language c volatile security definer;
CREATE FUNCTION qbim_close(
obj_id int8, tab text,
id uuid,
ed timestamp,
userid uuid)
returns int8 as 'MODULE_PATHNAME','close_global' language c volatile security definer;
В параметре ed указывается необходимое время для закрытия версий. Если пропущено
(указано null::timestamp
), то устанавливается текущее время. Остальные параметры
аналогичны параметрам в методах qbim_create
и qbim_change
.
При закрытии объекта, если это указано в obj, будет зафиксировано действие в таблице action.