Это плохая практика - иметь столбец «Состояние записи» в таблице базы данных?

12

Сначала я должен уточнить, что столбец состояния не предназначен для отражения статуса элемента реального мира, представленного записью (строкой) в таблице. Скорее, он предназначен для отображения статуса самой записи.

Он может быть простым, например, активным / неактивным, или сложным, например, «Утверждено», «Удалено», «Заблокировано», «Ожидание», «Отклонено» и т. Д. Статус может быть сохранен в столбце «логическое / короткое целое число» или в столбце с одним символом, с отображениями, такими как « true/ 1A= Одобрено.

Основная идея заключается в том, чтобы иметь в приложении поддержку восстановления корзины, похожую на корзину, и моделировать ее в базе данных. Если есть интерфейсный графический интерфейс или другой интерфейс, который может предположительно позволить пользователю «удалять» записи, он фактически не удаляет записи в таблице, а просто меняет статус записи на «Неактивно» или «Удалено». Когда интерфейс выбирает записи, он всегда получает записи, которые соответствуют только условию, что статус «Активный» или «Одобренный».

Если пользователь делает ошибку, и «удаленная» запись (с точки зрения пользователя) должна быть восстановлена, администратор базы данных может легко исправить запись как активную или утвержденную, что было бы лучше, чем поиск резервных копий и, надеюсь, поиск исходной записи. там. Либо сам интерфейс может позволить пользователю просматривать удаленные записи в отдельном представлении и восстанавливать их по мере необходимости, либо даже окончательно удалять их (удаляя фактическую запись).

Мои вопросы:

  • Это хорошая практика или плохая практика?
  • Влияет ли это на нормализацию данных?
  • Каковы потенциальные подводные камни?
  • Есть ли альтернативный метод достижения той же цели? (смотрите примечание)
  • Как можно заставить базу данных применять уникальные ограничения к данным только для определенного статуса (но разрешить любое количество дубликатов для других статусов)?
  • Почему базы данных не предоставляют функцию, похожую на «корзину», или отслеживание / восстановление таблиц, поэтому мы можем позволить интерфейсам без проблем удалять реальные записи?

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

ADTC
источник
Проблема с уникальными ограничениями (которые вы уже назвали) состоит именно в том, почему таблицы истории часто предпочтительнее - вы можете сохранить ограничения уникальных ключей в исходных таблицах и не добавлять их в таблицу истории. Более того, отдельные таблицы истории позволяют легче использовать для них определенные (зависящие от БД) параметры хранения, поэтому они часто лучше с точки зрения хранения, а не хуже. Когда у вас много таких таблиц, таблицы триггеров и истории не должны быть написаны от руки, а должны быть сгенерированы, что решит проблему поддержания их актуальности.
Док Браун

Ответы:

5

Я знаю это как «мягкое удаление»; просто пометить запись как «удаленную», даже если это не так.

Это хорошая практика или плохая практика?

По-разному.
Если это то, что нужно вашим пользователям [много], то, вероятно, это хорошо. Однако в подавляющем большинстве случаев я бы сказал, что это добавляет [много] накладных расходов для небольшой выгоды.

Влияет ли это на нормализацию данных?

Нет, но это будет влиять на ваше индексирование этих данных.
Убедитесь, что вы включили «удаленный» столбец в свои индексы, чтобы эти строки были исключены как можно раньше в ваших запросах.

Каковы потенциальные подводные камни?

Ваши данные становятся немного сложнее. Все, что находится рядом с данными, должно «знать» об этих дополнительных, «не очень-то» записях. Или вы должны создать представления для тех таблиц, которые исключают эти строки, и использовать эти представления, например, в своем инструменте выбора отчетов.

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

Есть ли альтернативный метод достижения той же цели? (смотрите примечание)

Не на самом деле нет.

Как можно заставить базу данных применять уникальные ограничения к данным только для определенного статуса (но разрешить любое количество дубликатов для других статусов)?

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

Альтернативой является использование триггеров, фрагментов процедурного кода, которые обеспечивают ссылочную целостность между таблицами и делают все умные, условные вещи, которые вам нужны. Это хорошо для вашего конкретного случая, но большинство преимуществ декларативного RI вылезают за пределы окна - между вашими таблицами нет [внешне] обнаруживаемых связей; это все «спрятано» в триггерах.

Почему базы данных не предоставляют функцию, похожую на «корзину», или отслеживание / восстановление таблиц, поэтому мы можем позволить интерфейсам без проблем удалять реальные записи?

Почему бы это?

В конце концов, это базы данных, а не файловые системы или электронные таблицы.

Что они делают, они [могут] делать очень, очень хорошо.

То, что они не делают, вероятно, не было большого спроса на.

Фил В.
источник
Хороший ответ, но есть альтернативные варианты, например, переместите строки в таблицу резервного копирования, откуда вы можете их восстановить. Таблица резервных копий может иметь минимальные индексы. Это сводит к минимуму проблемы, которые вы замечаете при существующем подходе (больший индекс, потенциальная путаница для пользователей таблицы и т. Д.), Но, очевидно, добавляет тот факт, что у вас есть другая таблица для обслуживания (и это означает, что записи переносятся в ссылки на внешние ключи). Есть довольно много других вариантов, но на самом деле все, что приходит на ум, - это некая общая реализация, а не нечто общее, предоставляемое каждой базой данных SQL для таких случаев.
Фрэнк Хопкинс
9

Это практика. Хорошо это или плохо, сильно зависит от вашего приложения и от того, насколько часто вам действительно нужно / нужно делать «восстановление». Я был бы весьма сомнителен в плане размещения такого рода столбцов в каждой таблице в системе - очень маловероятно, что вы действительно потрудитесь реализовать undelete для каждой таблицы в системе. И это требует реализации - в подавляющем большинстве случаев вы не удаляете ни одной строки из одной таблицы, вы должны проходить дочерние таблицы, удаляя строки и обновляя связанные таблицы.

Для большинства остальных вопросов это сильно зависит от реализации. Например, Oracle предоставляет различные методы для отслеживания всех изменений в таблице - Flashback Data Archive (FDA, также известный как Total Recall), являющийся самым последним подходом к ведению полной истории каждой версии строки и архивации в базе данных для реализации шаблон мягкого удаления. Другие базы данных могут предоставлять другие способы реализации шаблона. В зависимости от базы данных и того, как вы реализуете «мягкое» удаление, на производительность будут влиять различные факторы, могут ли ограничения применяться и как, и т. Д. Если мы говорим об Oracle, вы можете многое сделать, например, с помощью индексов на основе функций. В SQL Server вы часто можете использовать отфильтрованные индексы для аналогичных целей.

Джастин Кейв
источник
Oracle Flashback - это идеальное решение для того, чего я хочу. Жаль, что это собственность Oracle.
ADTC
4

В системах MRP / ERP очень часто используется поле «помечено для удаления».

Например, можно пометить деталь или инвентарную запись, которая больше не продается как неактивная, но с ней все еще остаются невыполненные заказы. Реальное удаление записи может повлиять на заказы, которые еще не были отправлены, записи в Главной книге, которые еще не были опубликованы, таблицы истории, которые не будут созданы до конца месяца, и т. Д. Многие системы запрещают удаление записи, если она не пройдет серию проверок в отношении других таблиц. Если вы каскадно удаляете свои отношения, реальное удаление может быть еще более разрушительным.

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

Аналогичный случай может быть сделан для этой функции на клиентской таблице и других «долгосрочных» таблицах. Это даже имеет смысл на более изменчивых таблицах, таких как заказы, хотя название флага может стать чем-то вроде «отправлено» или «отменено». Он выполняет ту же функцию: не удаляйте его в этот момент, а используйте его в качестве флага для программы очистки, чтобы попытаться проверить удаление записи в будущем.

Майк поддерживает Монику
источник
3

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

(Я не думаю, что это то, что вы имели в виду под «таблицей истории», что, я думаю, вы имели в виду, просто копируя измененные или удаленные записи в другую таблицу перед их изменением)

Жюль
источник
Интересная концепция. Я посмотрю, как это можно реализовать.
ADTC
1

Я часто вижу и использую этот шаблон для следующих случаев использования:

  • метаданные, в которых вы хотите отображать только те значения, которые действуют сегодня. Например, чтобы выбрать из списка производителей автомобилей в раскрывающемся списке, где enabled = 1, значения таблиц для ID, VALUE, ENABLED: 1, «Ford», 1 и 2, «Edsel», 0, 3, «Toyota» , 1 дает только выбор Ford и Toyota
  • для системы управления делами, где парадигма заключается в том, что дело может быть только в одном состоянии одновременно. В этом случае столбец переключения назывался CURRENT со значениями 0 или 1, навязанными проверочными ограничениями. По мере перемещения дела из одного состояния в другое приложение обновляет флаг CURRENT старого состояния до 0, а нового - до 1

Проблема состоит в том, чтобы обеспечить целостность данных, если более чем одно приложение или веб-служба выполняет запись в таблицы. Как вы гарантируете, что для дела существует только одно текущее состояние? Как указывает Джастин Кейв, это можно сделать в Oracle, создав виртуальный индекс на основе функции, но это дополнительные затраты на то, что изначально казалось простой концепцией.

kevinsky
источник
1

Это хорошая практика, если вы планируете использовать свои данные для отчетности (любое достаточно большое приложение должно иметь отчеты).

Чтобы ускорить работу вашего приложения, вам не следует запускать инструменты отчетности в вашей базе данных. Таким образом, вам нужно будет сделать копию / синхронизировать с другой базой данных.

Я использую recordStatusтолько два состояния ACTIVEили CANCELLEDв сочетании с lastUpdatedOnотметкой времени. Я использую, recordStatusа не то, statusчто обычно имеет деловое значение.

Когда я синхронизирую базу данных отчетов с приложением, я включаю фильтр, lastUpdatedOnчтобы узнать, какие из них я собираюсь заменить на стороне отчетов.

Что касается отчетности, у меня не будет полей recordStatusили, lastUpdatedOnпоскольку о них вообще не будет сообщено. Таким образом, когда я вижу CANCELLEDсостояние, я удаляю запись со стороны отчетности таким образом, что она имеет только активные записи.

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

Обратите внимание , ваш пример Approved, New, Pendingэто не очень хорошая идея , чтобы положить в общем поле , как есть бизнес означает , что он должен идти только туда , где это имеет смысл бизнес мудро.

Что касается заблокированных, используйте, versionNoкоторый обеспечивает оптимистическую блокировку для вашей записи.

Другой вариант вместо recordStatusэто recordActiveи хранят его в виде , booleanкоторый занимает меньше места и меньше индексации, но я бы беспокоиться о потребностях в будущем , что вы не можете предвидеть.

Архимед Траяно
источник