Какова соответствующая индексная архитектура, когда она вынуждена реализовать IsDeleted (мягкое удаление)?

16

В настоящее время у нас есть база данных и приложение, которое полностью функционально. У меня нет возможности изменить архитектуру на этом этапе. Сегодня каждая таблица в базе данных имеет поле «IsDeleted» NOT NULL BIT со значением по умолчанию «0». Когда приложение «удаляет» данные, оно просто обновляет флаг IsDeleted до 1.

Что мне трудно понять, так это то, как индексы в каждой из таблиц должны быть структурированы. Прямо сейчас каждый запрос / соединение / и т. Д. Всегда реализует проверку IsDeleted. Это стандарт, которому должны следовать наши разработчики. При этом я пытаюсь определить, нужно ли изменить все мои кластерные индексы первичного ключа в каждой из таблиц, чтобы включить первичный ключ И поле IsDeleted BIT. Кроме того, так как каждый запрос / присоединиться / и т. Д. должен реализовывать проверку IsDeleted. Является ли уместным предположение, что КАЖДЫЙ ОДИН индекс (также не кластеризованный) должен включать поле IsDeleted в качестве первого поля индекса?

Еще один вопрос, который у меня есть, касается отфильтрованных индексов. Я понимаю, что я мог бы наложить фильтры на индексы, такие как "WHERE IsDeleted = 0", чтобы уменьшить размер индексов. Однако, поскольку в каждом соединении / запросе должна быть реализована проверка IsDeleted, помешает ли это использованию отфильтрованного индекса (поскольку в соединении / запросе используется столбец IsDeleted)?

Помните, у меня нет возможности изменить подход IsDeleted.

Philᵀᴹ
источник

Ответы:

13

Здесь проще всего оставить свои ключи и кластеризованные индексы в покое и использовать отфильтрованные индексы для некластеризованных индексов.

Кроме того, вы можете перенести несколько больших таблиц в многораздельные кучи или многораздельные кластерные хранилища столбцов (SQL Server 2016+), оставив первичный ключ и уникальные индексы неразделенными. Это позволит вам помещать неключевые столбцы для строк IsDeleted в отдельную структуру данных, которая может быть дополнительно сжата по-другому или сохранена в другой файловой группе.

И убедитесь, что разработчики используют литерал вместо параметра для фильтрации строк IsDeleted. С параметром SQL Server должен использовать один и тот же план запросов для обоих случаев.

НАПРИМЕР

SELECT ... WHERE ... AND IsDeleted=0

И не:

SELECT ... WHERE ... AND IsDeleted=@IsDeleted

Использование параметра предотвратит использование отфильтрованного индекса и может привести к проблемам с анализом параметров.

Дэвид Браун - Microsoft
источник
Учитывая вездесущность и важность IsDeletedстолбца, независимо от физического хранилища, возможно, имеет смысл выставить данные через два представления (необязательно в разных схемах), решая как проблему параметризации, так и ошибки при доступе к данным, которые не должны были доступ менее вероятен. Доступ к базовым данным имеет отношение только к тем редким случаям, когда удаленные и не удаленные данные необходимо каким-либо образом объединять, и когда строки действительно необходимо переключить на «удаленные».
Мостерт
@JeroenMostert хороший совет. Здесь также можно использовать RLS или что-то вроде глобальных фильтров запросов EF Core. docs.microsoft.com/en-us/ef/core/querying/filters
Дэвид Браун - Microsoft
9

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

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

Другим вариантом является создание индексированного представления, которое может использоваться рядом различных запросов, которое фильтруется только для не удаленных строк. Это может быть особенно полезно в Enterprise Edition, где автоматическое сопоставление индексированных представлений работает без NOEXPANDподсказки.

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

Джош Дарнелл
источник
2

При разумном допущении, что удаление происходит редко, изменения индексов не являются подходящим решением.

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

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

Джошуа
источник
0

Я видел систему, где флаг IS_DELETED равен либо 0, либо значению PK. В других системах это был минус ПК.

Так как большинство запросов извлекали значения по «естественному» или бизнес-(иногда многопольному) ключу, они никогда не запрашивались PK, кроме как через соединения; но они всегда добавляли AND IS_DELETED = 0 в конце для основной таблицы и для любых соединяемых таблиц.

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

Рик Райкер
источник
0

Надеюсь, у вас есть право и возможность изменить запрос.

Однако, поскольку в каждом соединении / запросе должна быть реализована проверка IsDeleted, помешает ли это использованию отфильтрованного индекса (поскольку в соединении / запросе используется столбец IsDeleted)?

Я хотел сказать один важный момент, надеюсь, я смогу объяснить это.

В сложном запросе, где Transaction tableи Masterтаблицы используются оба.

Использовать IsDeleted=0только в Transactionтабл. Не используйте в Masterтаблице.

Пример,

Select * from dbo.Order O
inner join dbo.category C on o.categoryid=o.categoryid
inner join dbo.Product P on P.Productid=o.Productid
where o.isdeleted=0

Там нет смысла c.isdeleted=0(используя в Categoryтаблице). Это не нужно.

Точно так же есть ли смысл в использовании P.isdeleted=0?

Потому что я хочу все неопубликованные ордера и их детали.

Как можно Productудалить, когда Orderесть Activeили где Productidесть ссылка.

Таким образом, если вы тщательно отлаживаете важный запрос, возможно, вы можете удалить часть isdeleted = 0.

Не слепо создавайте фильтрованный индекс, сначала выберите все эти очень важные и медленные запросы.

Оптимизируйте эти медленные запросы, а затем принимайте решение только о фильтрованном индексе или настройке индекса.

KumarHarsh
источник