Расширение Qbim, пример реализации битемпоральной модели данных

Примеры:


Предпосылки

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

Шестая нормальная форма введена Кристофером Дейтом как обобщение пятой нормальной формы для хронологической базы данных в своей книге Введение в системы баз данных.

Расширение Qbim представляет собой конструктор для разработки ERM на темпоральной основе, то есть, моделируемые объекты представляются временными срезами, последовательностями своих состояний во времени. Основное внимание и акцент в данном расширении сделан не на "декомпозиции до конца", а на поддержании темпоральности данных. Расширение реализует краткий набор логики хранения (создания, изменения) объектов в реляционной СУБД (на примере qhb), реализованный в слое хранимых процедур.


Модель данных

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

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

Серия данных с одним идентификатором (далее объект), может образовывать непересекающиеся отрезки, или «версии», на временной оси «действия» этих данных (business time).

Дополнительная временная ось (system time) фиксирует, когда эти версии были созданы или изменены.


Реализация модели

Терминология

ТерминОпределение
ОбъектНекоторая оцифрованная, смоделированная из реального мира сущность. Обладает свойствами (атрибутами) и операциями над ними (методами). В определённые временные интервалы имеет фиксированный набор атрибутов. Но во времени, от интервала к интервалу, может менять набор атрибутов. В базе данных набор Объектов представляется сущностью/таблицей, его атрибуты представлены полями таблицы, методы реализованы хранимыми процедурами и функциями.
Реализация объектаОдин из представителей объекта, выделяемый среди других с помощью идентификатора. В базе данных представляет собой серию записей одной таблицы/сущности.
Версия реализации объектаОдин из наборов атрибутов объекта, постоянный в течении определенного временного интервала. В базе данных - одна из записей таблицы/сущности, принадлежащая объекту (по идентификатору), с определенным интервалом действия.
Идентификатор реализации объектаИдентификатор (id - обычно целое число, но может быть UUID-ом), однозначно определяющий реализацию объекта среди других объектов сущности. Все версии одного и того же объекта имеют один и тот же идентификатор.
Идентификатор версии реализации объектаКортеж из идентификатора и интервала действия версии ( <id[, sd, ed]> ), однозначно определяющий объект и версию.

Ограничения и умолчания

Зарезервированные поля

Для идентификатора и границ интервала действия в версионированных таблицах резервируются три поля:

Резервируемое полеТипНазначение
idbigint или uuidидентификатор
sdtimestampstart date, начало периода действия
edtimestampend 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:

ПолеТипНазначение
idbigserialИдентификатор
tabtextНазвание таблицы, где хранится объект. Если требуется, вместе с именем схемы
dsctextОписание
parentbigintСсылка на ID верхнего в иерархии объекта
is_temporalboolПризнак темпоральности объекта
is_loggingboolПризнак логирования действий пользователя для объекта
is_globalboolПризнак глобального идентификатора у объекта. Для глобальных идентификаторов используется тип UUID, для локальных — bigint

Таблица obj_item:

ПолеТипНазначение
idbigserialИдентификатор
upbigintСсылка на объект (obj.id)
ordbigintПорядковый номер атрибута
nametextИмя
r_clsbigintЕсли имеется словарное поле, номер словаря (@CLS.ID)
r_objbigintЕсли имеется ссылка на объект, номер объекта
optionjsonbДополнительные параметры. Например, указание о замене типа на массив для методов
dsctextОписание атрибута

Таблица action:

ПолеТипНазначение
idbigserialИдентификатор
r_objecturefСсылка на изменяемый объект и его идентификатор
lnk_dttimestampДата привязки к версии реализации события (SD)
fct_dttimestampРеальная дата версии
actionbigintПроизведенное действие. Словарь
databasetextБаза данных, где расположен измененный объект
schematextСхема, где расположен измененный объект
dbusertextПользователь базы данных, совершивший действие
pidtextИдентификатор процесса (Process ID), в рамках работы которого произведено изменение
useriduuidUUID пользователя, вошедшего в систему и совершившего действие

В таблицу action, если указано в obj.is_logging, записываются основания действий с объектами.

«Действие» (поле action) имеет следующую кодировку:

КодДействие
1Создание
2Изменение (при создании новой версии)
3Изменение (при сокращении периода действия)
4Закрытие версии

ВАЖНО!
Для функционирования приложения достаточно важно, чтобы записи в эту таблицу записывались максимально быстро. Для этого есть возможность создавать таблицу как unlogged, а при включенном TARQ — также ... using append_only.


Методы описания объектов

Для описания объектов в таблицах obj и obj_item предоставляется функция qbim_describe_object(...).

Параметры функции:

ПараметрТипЗначение по умолчаниюНазначение
i_table_nametextИмя объекта (таблицы), в комбинации с именем схемы или без него. При отсутствии явного указания на имя схемы таблица ищется в схемах, указанных в параметре search_path.
i_obj_idbigintНомер объекта. Если указать null, номер будет присвоен автоматически из последовательности для obj.id
i_parent_obj_idbigintnullНомер родительского объекта, если создаётся иерархия объектов.
i_temporalbooltrueПризнак версионированного объекта (темпоральности). В противном случае - неверсионированный, такие тоже можно описывать в таблицах объектов.
i_loggingbooltrueПризнак логирования изменений в таблице регистрации действий action
i_globalboolfalseПризнак "глобальности" идентификатора экземпляра объекта (тип uuid), в противном случае идентификатор "локальный" (тип bigint/int8).

При описании используются комментарии таблицы и её полей.

При заполнении описания полей объекта в комментарии могут применяться шаблоны ссылок на словари и связанные объекты (поля r_cls и r_obj в таблице obj_item). Шаблоны соответственно - CLS#[[:digit:]] и OBJ#[[:digit:]].

Например, следующий комментарий:

COMMENT ON COLUMN object_tbl.r_domain IS 'Ссылка на домен. OBJ#22';

при описании объекта заполнит в obj_item для данного поля указание, что он является ссылкой на другой объект:

idupordnamer_clsr_objoptiondsc
.........r_domain...22...Ссылка на домен. OBJ#22

а комментарий:

COMMENT ON COLUMN object_tbl.model_status IS 'Состояние когнитивной модели. CLS#18';

при описании объекта заполнит в obj_item для данного поля указание, что поле словарное:

idupordnamer_clsr_objoptiondsc
.........model_status18......Состояние когнитивной модели. CLS#18

Методы работы с объектами

Для создания, изменения и закрытия объектов предлагаются методы, которые корректно отслеживают ограничения модели.

Методы выборки оставляются на выбор пользователя или приложения. Возможно выбрать конкретную версию, действующую в указанный момент времени, или серии версий за указанный период вместе со схлопнувшимися версиями (промежуточные версии с нулевой длительностью, 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.

Примечание
Для неверсионированных объектов точно также доступно описание в таблицах obj и obj_item, а также с ними работают методы создания, изменения и закрытия объектов. Для неверсионированных объектов системным является только один атрибут - идентификатор (id). В соответствующих методах параметры sd и ed не имеют смысла и игнорируются. Метод изменения подразумевает простой update экземпляра объекта. Метод закрытия подразумевает удаление экземпляра объекта. Регистрация действий (в таблице action) также производится, но поля lnk_dt и fct_dt заполняются одним и тем же значением - временем регистрации действия.

Установка расширения

Установка расширения производится командой CREATE EXTENSION. Поскольку есть расширения, от которых зависит расширение Qbim, рекомендуется использовать установку с предложением CASCADE. Расширение Qbim переносимое, поэтому его можно установить в различные схемы базы данных, используя SCHEMA имя_схемы.

Начиная с версии расширения 1.2 есть возможность использовать различные опции таблицы action. Для управления созданием этой таблицы используется группа локальных параметров (см. Таблицу ниже).

При обновлении расширения до версии 1.2 с предыдущих версий используйте предложение UPDATE команды ALTER EXTENSION.

Таблица Локальных параметров для управления созданием таблицы action

ПараметрНазначениеЗначение по-умолчанию
qbim.action.use_partitionsСоздавать таблицу партиционированную по полю fct_dt.'true'
qbim.action.partition_period- при создании партиционированной таблицы - размер секции, которые будут созданы при создании. Возможны варианты: 'day', 'week', 'month', 'year'.'month'
qbim.action.partition_count- при создании партиционированной таблицы - количество создаваемых секций, не считая двух секций для "самых старых" и "самых новых" значений.5
qbim.action.use_unloggedСоздавать таблицу с параметром unlogged. Внимательно отнеситесь к этому параметры, его лучше не использовать при работе в кластере.'false'
qbim.action.use_appendonlyСоздавать таблицу как неизменяемую (append_only). Возможно только при включённом TARQ.'true'
qbim.action.upgrade_existsМигрировать данные из уже существующей таблицы action, при обновлении с ранних версии расширения Qbim, до версии 1.2. Если расширение устанавливается впервые, параметр не имеет смысла.'true'

Параметры нужно установить перед вызовом команд CREATE EXTENSION или ALTER EXTENSION.

Примеры установки:

create extension qbim cascade;
set qbim.action.use_partitions='true';
set qbim.action.partition_period='year';
set qbim.action.partition_count=2;

create extension qbim schema my_schema cascade;