Приёмы хранения данных, изменяющихся со временем

Каверзный вопрос на собеседовании: бутылка водки стоит 100 рублей, но в период январских праздников – 120. Опишите структуру хранения данных в SQL.

Приём 1: valid_to.

Все мы привыкли пользоваться ORM, многие из которых по-дефолту создают столбцы created_at и updated_at. Однако добавьте ещё третий аналогичный столбец valid_to, в котором укажите дату “срока годности”, добавьте дефолтный scope в ORM – и “протухшие товары” просто не будут попадать в ваши выборки.

Элегантно и просто, словно установка времени жизни кэша. Сюда укладываются временно действующие привилегии доступа, временные возможности или ачивки в играх, маркетинговые акции и так далее. Тем более, что для администратора системы все “устаревшие” данные остаются доступными.

Приём 2: использование даты в сущности связи в many-to-many.

Один любитель путешествовать некоторое время жил в Петербурге, потом переехал в Москву и затем улетел в Лондон. Вычислите время, которое он жил в указанном городе, например в Москве.

Моя практика собеседований показывает, что более 50% кандидатов, сталкиваясь с этой задачей впервые, добавляют столбец city_id в таблицу users.

Для упрощения предположим, что переезды “мгновенные” (автор предлагает читателю учесть их длительность в качестве домашнего задания). В таком случае, мы имеем две модели: User и City, а так же модель связи многие-ко-многим (назовём её UserCityLive) между ними.

Располагая дефолтным created_at в таблице user_city_lives, мы легко можем найти две записи (строка связи user==Москва и строка, следующая за ней). А затем просто вычислить дельту между ними.

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

Приём 3: история версий документа.

Забудьте про операции Update и каждый раз делайте новый Insert. Если вы пишете пост в блог – это даст вам историю черновиков документа. Если вы проводите документ по цепочке узлов бизнес-процесса (workflow) – это даст вам хранение состояний документа по-отдельности на каждом рабочем месте, где над ним производилась работа.

Приём 4: разная стоимость бутылки водки.

Вряд ли интересно как-то выделять именно цену – ведь любой параметр может быть подвержен временны́м изменениям. Здесь нам поможет модель EAV, которая базируется на трёхколоночной таблице: ID объекта, ID свойства, значение (в некоторых системах значения выносятся в отдельный справочник, и здесь указывается ID значения).

Если добавить к этой модели ещё две колонки (valid_from и valid_to), то мы получим следующий эффект:

Объект “бутылка водки” : Свойство “цена” : Значение 100 : От 01 января : До 31 декабря
Объект “бутылка водки” : Свойство “цена” : Значение 120 : От 01 января : До 11 января

Готово! Мы сохранили разные значения параметра в зависимости от разных временны́х периодов. Вопрос, как именно стоит выбирать корректный период в случае их явного пересечения, остаётся на совести конкретных реализаторов: лично мне больше симпатичен принцип “самый короткий интервал – самый точный“, однако маркетологи наверняка укажут ORDER BY price DESC.

Какие ещё приёмы вы применяете? Пишите в комментариях.