Удаление записей не на совсем?

Впервые разрабатываю систему управления неким предприятием, так же впервые столкнулся с такой проблемой как удаление данных из БД.


Расскажу о сути проблемы через пример:


Необходимо разработать несколько модулей, два из которых: Прайс-лист и журнал продаж.

Изменяющийся ассортимент услуг и товаров для каждой компании это нормально, позиции в прайсе могут удаляться и создаваться. Насчет последнего все просто. Создал новую запись — и дело в шляпе, однако с удалением не все так просто: Трудно будет объяснить аналитикам, почему же они видят строки «22.07.2010 было продано 228 штук „товар ненайден“» в своем журнале.


Тут мы подходим к такой теме как логическое удаление (или soft delete).

Звучит заманчиво: универсальное решение, которое позволяет помечать строки в БД как удаленные, и тем не менее физически не удалять их. Только вот на практике это может превратить любой запрос, даже самый тривиальный, в кладезь ошибок и boilerplate кода.


Есть вариант сделать таблицу/БД-двойник, в которую будут перемещаться удаленные записи. Те запросы которые нуждаются в удаленных записях должны явно указывать при обращении к DAO параметр necroModeOn. Ну или делать отдельные методы/реализации DAO для доступа к архивным таблицам/БД.


Но мне почему-то (vanga.jpg) кажется что в этом решении есть свои подводные камни.


Добрейшие, подскажите пожалуйста, как лучше поступить в данной ситуации?
  • Если вы реализовывали классическое логическое удаление (WHERE deleted = 0) то расскажите насколько вы намучились (и намучились ли?)?
  • Насколько хорош вариант с VIEW?
  • Может быть в MySQL есть какие-нибудь волшебные фичи для таких ситуаций?


UPDATE:


Хочу немного уточнить, вопрос, почему же мне все таки не нравится вариант с дополнительным полем с флагом удаления (или временем удаления или статусом — одно и то же).


Мне хочется сравнить этот подход со способом обработки ошибок в современных императивных языках (try-catch-finally) и подходом с возвращением кода ошибки.

Подход с возвращением кода ошибки захламляет код и логику программы. Стоит один раз забыть проверить код ошибки и неясно чего ожидать.


Тут похожая ситуация. Я себе представляю кучу способов прострелить себе коленку с помощью такого подхода.


Кроме того, это ухудшает читабельность запросов и усложняет устройство DAO. Это мешает в одной таблице мертвецов и живые строки, что может негативно повлиять на производительность в горячих таблицах. В join'ах вышеперечисленное ×2.
  • Вопрос задан
  • 6019 просмотров
Решения вопроса 1
ivnik
@ivnik
1) Если беспокоит производительность, то удалённые записи переносить в отдельную таблицу.
2) Если объёмы таблиц не большие/не требуется высокая производительность, то делать как посоветовали выше, дополнительным флагом. В этом случае лучше сделать view, чтобы уменьшить объём рефакторинга, если захочется поменять логику (т.е. например вынести удалённые записи в отдельную таблицу или изменить способ работы с флагом удалённых записей). Сам флаг я бы делал NOT NULL и естественно добавил бы в индексы.

P.S. у меня был похожий случай, когда надо было хранить ссылки на удалённые записи, мы использовали hibernate + envers (java). Классы/поля с аннотацией @Audited порождали name_AUD таблицы, в которых хранились все те же данные, плюс два дополнительных поля — номер ревизии и тип ревизии (создание, изменение, удаление). Существовала отдельная таблица с информацией о ревизиях с полями номер ревизии, дата ревизии и дополнительные наши поля (например логин, ip-адрес). Одной транзакции соответствовала одна ревизия. Хотя, возможно этот вариант для вашей задачи слишком навороченный, привёл его просто для примера альтернативного варианта.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 6
docomo
@docomo
> Если вы реализовывали классическое логическое удаление (WHERE deleted = 0) то расскажите насколько вы намучились (и намучились ли?)?

А в чем могут быть мучения-то?
Запись в базе, помеченная флагом удаления, который может использоваться при выборках.
Ответ написан
Комментировать
Я всегда дополнял полем deleted timestamp, при удалении вносил CURRENT. Соответственно знаем когда удалили запись.
deleted is null — запись не удалена
Ответ написан
Jeket
@Jeket
Если есть система статусов товаров, добавьте еще один статус — удален, но не отображайте эти товары. Как вариант.
Ответ написан
Комментировать
tyzhnenko
@tyzhnenko
System Administrator, DevOps, QA Engineer
В какой-то момент пришли к использованию Sphinx, а дальше все проще, большая часть запросов к данным идет по PK. Вся выборка и поиск идет через Sphinx.
Индекс не содержит данных с флагом deleted.
Ответ написан
Комментировать
@rusevgen
Если база большая, часто что-то меняется и есть опасность упереться в производительность, то я вытаскивал удаленные данные в отдельную таблицу, но после определенного времени, то есть сначала помечаем флагом, по истечении месяца перетаскиваем в отдельную таблицу-близнец. Просто был случай что вроде «старые» данные иногда и нужны, но огромное количество «мертвых душ» очень сказывалось загруженности сервера.

Если вероятность необходимости обработки старых удаленных данных мала, то пусть они лучше лежат отдельно, если же операции с ними происходят часто, то нужно оставлять их (или переносить только когда критерий «бесполезности» выполнен — у меня это был месяц без изменений)
Ответ написан
Комментировать
gaelpa
@gaelpa
Быть может метод с флагом будет казаться не таким нелогичным, если называть его не «deleted», а что-нибудь вроде «available_in_price_list»?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы