Временная дизайн базы данных, с поворотом (живые против черновых строк)
-
26-10-2019 - |
Вопрос
Я ищу в реализации объективной версии с дополнительным поворотом необходимости иметь как живые, так и черновые объекты, и могу использовать идеи от кого-то, кто испытывает в этом, так как я начинаю задаваться вопросом, возможно ли это даже без потенциально ужасных хаков.
Я сломаю его на посты с тегами ради примера, но мой вариант использования немного более общий (включает в себя медленно изменяющиеся размеры - http://en.wikipedia.org/wiki/slowly_changing_dimension).
Предположим, у вас таблица сообщений, таблица тегов и таблица Post2tag:
posts (
id
)
tags (
id
)
post2tag (
post_id fkey posts(id),
tag_id fkey tags(id)
)
Мне нужна пара вещей:
- Возможность показать, как именно выглядит пост в произвольном времени, в том числе для удаленных строк.
- Следите за тем, кто редактирует, что, для полной аудиторской тропы.
- Нужен набор материализованных представлений («живые» таблицы) для поддержания ссылочной целостности (то есть ведение ведения регистрации должно быть прозрачностью для разработчиков).
- Нужно быть соответствующим образом быстро для жизни а также Последние черновые ряды.
- Возможность иметь проект пост сосуществует с живым постом.
Я исследовал различные варианты. До сих пор лучшее, что я придумал (без очков № 4/ #5), похоже на настройку SCD Type6-Hybrid, но вместо того, чтобы иметь текущий логический, существует материализованный вид для текущего ряда. Для всех намерений и целей, это выглядит так:
posts (
id pkey,
public,
created_at,
updated_at,
updated_by
)
post_revs (
id,
rev pkey,
public,
created_at,
created_by,
deleted_at
)
tags (
id pkey,
public,
created_at,
updated_at,
updated_by
)
tag_revs (
id,
public,
rev pkey,
created_at,
created_by,
deleted_at
)
post2tag (
post_id fkey posts(id),
tag_id fkey tags(id),
public,
created_at,
updated_at,
updated_by
)
post2tag_revs (
post_id,
tag_id,
post_rev fkey post_revs(rev), -- the rev when the relation started
tag_rev fkey tag_revs(rev), -- the rev when the relation started
public,
created_at,
created_by,
deleted_at,
pkey (post_rev, tag_rev)
)
Я использую pg_temporal для поддержания индексов в период (create_at, deleted_at). И я держу различные таблицы в синхронизации с использованием триггеров. Yada Yada Yada ... Я создал триггеры, которые позволяют отменить редактирование на посты/теги таким образом, чтобы проект хранился в Revs без публикации. Это прекрасно работает.
Кроме Когда мне нужно беспокоиться о отношениях, связанных с черновыми строками, на Post2tag. В этом случае весь ад вырвался на свободу, и это намекает на меня, что у меня есть какая -то проблема дизайна. Но у меня заканчиваются идеи ...
Я рассматривал возможность представления дублирования данных (то есть строки Post2TAG, представленные для каждого проекта пересмотра). Этот вид работы, но, как правило, намного медленнее, чем я хотел бы.
Я подумал о внедрении таблиц черновиков для «последнего проекта», но это быстро становится очень уродливым.
Я рассматривал всевозможные флаги ...
Итак, вопрос: существует ли общепринятое средство управления живыми и неживыми рядами в среде, контролируемой строкой? А если нет, то с чем вы пробовали и были достаточно успешными?
Решение 4
Думаю, я прибил это. По сути, вы добавляете (уникальное) черновое поле в соответствующие таблицы, и вы работаете над черновиками, как будто они были новым постом/тегом/и т. Д.:
posts (
id pkey,
public,
created_at stamptz,
updated_at stamptz,
updated_by int,
draft int fkey posts (id) unique
)
post_revs (
id,
public,
created_at,
created_by,
deleted_at,
pkey (id, created_at)
)
tags (
id pkey,
public,
created_at,
updated_at,
updated_by,
draft fkey tags (id) unique
)
tag_revs (
id,
public,
created_at,
created_by,
deleted_at,
pkey (id, created_at)
)
post2tag (
post_id fkey posts(id),
tag_id fkey tags(id),
public,
created_at,
updated_at,
updated_by,
pkey (post_id, tag_id)
)
post2tag_revs (
post_id,
tag_id,
public,
created_at,
created_by,
deleted_at,
pkey (post_id, tag_id, created_at)
)
Другие советы
Якорное моделирование это хороший способ реализовать временный DB - см. Википедия статья слишком. Требуется некоторое время, чтобы привыкнуть, но работайте хорошо. Есть Инструмент онлайн -моделирования и если вы загрузите поставляемый файл XML [File -> Load Model from Local File]
Вы должны увидеть что -то подобное - также используйте [Layout --> Togle Names]
.
А [Generate --> SQL Code]
будет производить DDL для таблиц, видов и функций времени. Код довольно длинный, поэтому я не публикую его здесь. Проверьте код - вам может потребоваться изменить его для своего БД.
Вот файл для загрузки в инструмент моделирования.
<schema>
<knot mnemonic="EXP" descriptor="Expired" identity="smallint" dataRange="char(1)">
<identity generator="true"/>
<layout x="713.96" y="511.22" fixed="true"/>
</knot>
<anchor mnemonic="US" descriptor="User" identity="int">
<identity generator="true"/>
<attribute mnemonic="USN" descriptor="UserName" dataRange="varchar(32)">
<layout x="923.38" y="206.54" fixed="true"/>
</attribute>
<layout x="891.00" y="242.00" fixed="true"/>
</anchor>
<anchor mnemonic="PO" descriptor="Post" identity="int">
<identity generator="true"/>
<attribute mnemonic="TIT" descriptor="Title" dataRange="varchar(2)">
<layout x="828.00" y="562.00" fixed="true"/>
</attribute>
<layout x="855.00" y="471.00" fixed="true"/>
</anchor>
<anchor mnemonic="TG" descriptor="Tag" identity="int">
<identity generator="true"/>
<attribute mnemonic="TGT" descriptor="TagText" dataRange="varchar(32)">
<layout x="551.26" y="331.69" fixed="true"/>
</attribute>
<layout x="637.29" y="263.43" fixed="true"/>
</anchor>
<anchor mnemonic="BO" descriptor="Body" identity="int">
<identity generator="true"/>
<attribute mnemonic="BOT" descriptor="BodyText" dataRange="varchar(max)">
<layout x="1161.00" y="491.00" fixed="true"/>
</attribute>
<layout x="1052.00" y="465.00" fixed="true"/>
</anchor>
<tie timeRange="datetime">
<anchorRole role="IsTagged" type="PO" identifier="true"/>
<anchorRole role="IsAttached" type="TG" identifier="true"/>
<anchorRole role="BYAuthor" type="US" identifier="false"/>
<knotRole role="Until" type="EXP" identifier="false"/>
<layout x="722.00" y="397.00" fixed="true"/>
</tie>
<tie timeRange="datetime">
<anchorRole role="Contains" type="PO" identifier="true"/>
<anchorRole role="ContainedIn" type="BO" identifier="false"/>
<layout x="975.00" y="576.00" fixed="true"/>
</tie>
<tie>
<anchorRole role="CreatedBy" type="TG" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="755.10" y="195.17" fixed="true"/>
</tie>
<tie>
<anchorRole role="CreatedBy" type="PO" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="890.69" y="369.09" fixed="true"/>
</tie>
<tie>
<anchorRole role="ModifiedBy" type="BO" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="1061.81" y="322.34" fixed="true"/>
</tie>
</schema>
Я внедрил временную базу данных, используя правила и триггеры PostgreSQL, и завершил ее в автономный пакет для Activerecord: http://github.com/ifad/chronomodel
Однако дизайн не зависит от языка / структуры - вы можете создавать правила и триггеры вручную, а база данных позаботится об остальном. Посмотри на https://github.com/ifad/chronomodel/blob/master/readme.sql.
Также эффективная индексация и запрос временных данных с использованием геометрических операторов включены в качестве бонуса. :-)
У Post2tag_REVS есть проблема в том, что он пытается выразить 2 принципиально разные понятия.
Тэг, применяемый к револютному пересмотру Post, только когда -либо применяется к этой пересмотре, если не публикуется пересмотр.
Как только тег опубликован (т.е. связан с опубликованной ревизией Post), он применяется к каждому будущему пересмотру поста, пока он не будет отменен.
И связать с опубликованным пересмотром, или неасоцированием, не обязательно одновременно с публикацией пересмотра, если только вы искусственно исполняете это, клонируя пересмотр, чтобы вы могли связать дополнения или удаления тегов ...
Я бы изменил модель, сделав Post2tag_Revs.post_rev, актуальным только для черновых тегов. После того, как ревизия опубликована (и тег живой), я бы использовал столбец штампа времени, чтобы отметить начало и конец опубликованной достоверности. Вы можете или не можете хотеть новую запись после 2TAG_REVS представлять это изменение.
Как вы указываете, это делает эти отношения би-временный. Анкет Вы можете повысить производительность в «нормальном» случае, добавив логический в Post2tag, чтобы указать, что тег в настоящее время связан с постом.
Используйте только 3 таблицы: сообщения, теги и пост2TAG.
Добавьте столбцы start_time и end_time во все таблицы. Добавьте уникальный индекс для ключа, start_time и end_time. Добавьте уникальный индекс для ключа, где end_time - NULL. Добавить тригеры.
Для текущего:
SELECT ... WHERE end_time IS NULL
Вовремя:
WHERE (SELECT CASE WHEN end_time IS NULL
THEN (start_time <= at_time)
ELSE (start_time <= at_time AND end_time > at_time)
END)
Поиск текущих данных не является медленным из -за функционального индекса.
Редактировать:
CREATE UNIQUE INDEX ... ON post2tag (post_id, tag_id) WHERE end_time IS NULL;
CREATE UNIQUE INDEX ... ON post2tag (post_id, tag_id, start_time, end_time);
FOREIGN KEY (post_id, start_time, end_time) REFERENCES posts (post_id, start_time, end_time) ON DELETE CASCADE ON UPDATE CASCADE;
FOREIGN KEY (tag_id, start_time, end_time) REFERENCES tags (tag_id, start_time, end_time) ON DELETE CASCADE ON UPDATE CASCADE;