Я уверен, что многие приложения, критические приложения, банки и так далее делают это ежедневно.
Идея, стоящая за всем этим:
- все строки должны иметь историю
- все ссылки должны оставаться связными
- должно быть легко делать запросы, чтобы получить «текущие» столбцы
- клиенты, которые купили устаревшие вещи, все равно должны видеть то, что они купили, хотя этот продукт больше не является частью каталога
и так далее.
Вот что я хочу сделать, и я объясню проблемы, с которыми я сталкиваюсь.
Все мои таблицы будут иметь эти столбцы:
id
id_origin
date of creation
start date of validity
start end of validity
И вот идеи для операций CRUD:
- создать = вставить новую строку с
id_origin
=id
,date of creation
= сейчас,start date of validity
= сейчас,end date of validity
= ноль (= означает, что это текущая активная запись) - обновление =
- читать = читать все записи с
end date of validity
== ноль - обновить "текущую" запись
end date of validity
= null сend date of validity
= сейчас - создайте новый с новыми значениями, и
end date of validity
= null (= означает, что это текущая активная запись)
- читать = читать все записи с
- delete = обновить "текущую" запись
end date of validity
= null сend date of validity
= сейчас
Итак, вот моя проблема: со многими ко многим ассоциациям. Давайте рассмотрим пример со значениями:
- Таблица A (id = 1, id_origin = 1, начало = сейчас, конец = ноль)
- Таблица A_B (начало = сейчас, конец = ноль, id_A = 1, id_B = 48)
- Таблица B (id = 48, id_origin = 48, начало = сейчас, конец = ноль)
Теперь я хочу обновить таблицу A, id записи = 1
- Я отмечаю запись id = 1 с конца = сейчас
Я вставляю новое значение в таблицу A и ... черт, я потерял свое отношение A_B, если я тоже не дублирую отношение ... это закончится таблицей:
Таблица A (id = 1, id_origin = 1, начало = сейчас, конец = сейчас + 8мн)
- Таблица A (id = 2, id_origin = 1, начало = сейчас + 8 мин, конец = ноль)
- Таблица A_B (начало = сейчас, конец = ноль, id_A = 1, id_B = 48)
- Таблица A_B (начало = сейчас, конец = ноль, id_A = 2, id_B = 48)
- Таблица B (id = 48, id_origin = 48, начало = сейчас, конец = ноль)
И ... ну, у меня есть еще одна проблема: отношение A_B: я должен пометить (id_A = 1, id_B = 48) как устаревшее или нет (A - id = 1 устарело, но не B - 48)?
Как с этим бороться?
Я должен разработать это в больших масштабах: продукты, партнеры и так далее.
Каков ваш опыт в этом? Как бы вы сделали (как вы сделали)?
-- Редактировать
Я нашел эту очень интересную статью , но она не имеет дело с "каскадным устареванием" (= то, что я спрашиваю на самом деле)
источник
Ответы:
Мне не ясно, предназначены ли эти требования для целей аудита или просто для простого исторического справочника, такого как CRM и корзины покупок.
В любом случае, рассмотрите наличие таблиц main и main_archive для каждой основной области, где это требуется. «Main» будет иметь только текущие / активные записи, тогда как «main_archive» будет иметь копию всего, что когда-либо попадало в main. Вставка / обновление в main_archive может быть триггером из вставки / обновления в main. Удаление против main_archive может затем выполняться в течение более длительного периода времени, если вообще когда-либо.
Для проблем со ссылками, таких как Cust X купил продукт Y, самый простой способ решить вашу ссылочную проблему cust_archive -> product_archive - никогда не удалять записи из product_archive. Как правило, отток должен быть намного ниже в этой таблице, поэтому размер не должен быть слишком плохим.
НТН.
источник
LP_
, и каждая важная таблица имеет эквивалентLH_
, с триггерами, вставляющими исторические строки при вставке, обновлении, удалении. Это не работает для всех случаев, но это было хорошей моделью для того, что я делаю.UNION
представление, что позволяет вам делать интересные вещи, такие как принудительное применение уникального ограничения для текущих и исторических записей.id
" и "id_ref
".id_ref
это ссылка на актуальную идею таблицы. Пример:person
аperson_h
. уperson_h
меня есть "id
", и "id_ref
" гдеid_ref
связано с 'person.id
', поэтому у меня может быть много строк с одинаковымиperson.id
(= когда строкаperson
модифицируется), иid
все мои таблицы имеют автоматический ввод.Это имеет некоторое совпадение с функциональным программированием; в частности, концепция неизменности.
Вы называете одну таблицу,
PRODUCT
а другую -PRODUCTVERSION
или похожую. Когда вы меняете продукт, вы не обновляете его, а просто вставляете новуюPRODUCTVERSION
строку. Чтобы получить последнюю версию, вы можете индексировать таблицу по номеру версии (desc), метке времени (desc) или иметь флаг (LatestVersion
).Теперь, если у вас есть что-то, что ссылается на продукт, вы можете решить, на какую таблицу он указывает. Указывает ли это на
PRODUCT
сущность (всегда относится к этому продукту) или наPRODUCTVERSION
сущность (относится только к этой версии продукта)?Это становится сложным. Что делать, если у вас есть фотографии продукта? Они должны указывать на таблицу версий, потому что они могут быть изменены, но во многих случаях этого не произойдет, и вы не захотите дублировать данные без необходимости. Это означает, что вам нужен
PICTURE
стол и отношения «PRODUCTVERSIONPICTURE
многие ко многим».источник
Я реализовал все вещи из здесь с 4 полями , которые находятся на все мои таблицах:
Каждый раз, когда необходимо изменить запись, я дублирую ее, отмечаю дублированную запись как «старую» =,
date_validity_end=NOW()
а текущую - как хорошуюdate_validity_start=NOW()
иdate_validity_end=NULL
.Хитрость заключается в отношениях «многие ко многим» и «один ко многим»: она работает, не касаясь их! Это все о запросах, которые являются более сложными: чтобы запросить запись в точную дату (= не сейчас), я должен для каждого объединения и для основной таблицы добавить эти ограничения:
Так же с продуктами и атрибутами (многие ко многим относятся):
источник
Как насчет этого? Это кажется простым и довольно эффективным для того, что я сделал в прошлом. В вашей таблице «истории» используйте другой ПК. Итак, ваше поле «CustomerID» - это PK в вашей таблице «Customer», а в таблице «history» ваш PK - «NewCustomerID». «CustomerID» становится просто другим полем только для чтения. Это оставляет «CustomerID» неизменным в истории, и все ваши отношения остаются неизменными.
источник