SQL Delete, если на строку не влияют ограничения - PullRequest
5 голосов
/ 29 июня 2009

Первое замечание, что я видел этот вопрос: TSQL удалить с внутренним объединением

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

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

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

AND NOT EXISTS
(SELECT key 
FROM table 
WHERE table.key = centretable.key)

, который TSQL превращает в правильное анти-полусоединение и делает это красиво по индексам. Проблема в том, что он создает список материалов для удаления, а затем снова выполняет те же проверки, что и при удалении.

Полагаю, у меня вопрос: есть ли попытка удаления по строке (я не собираюсь делать это в курсоре, поскольку знаю, насколько медленным это будет), но вы думаете, что такое ключевое слово существует Хотя мне не повезло найти его.

Ответы [ 6 ]

2 голосов
/ 06 июля 2009

С точки зрения одной команды, которая проверяет отношения только один раз (а не дважды в вашем примере - один раз для NOT EXISTS, один раз для DELETE), тогда я ожидаю, что ответ большой жирный нет .

(от идеи стены): Если это серьезная проблема, вы можете попробовать какую-то реализацию подсчета ссылок, используя триггеры для обновления счетчика - но на самом деле я ожидаю, что это потребует гораздо больше усилий, чем простая проверка ключей, которые вы уже сделали. 1005 *

Вы также можете исследовать NOCHECK во время удаления (так как вы проверяете это самостоятельно); но вы можете сделать это только на уровне таблицы (так что, вероятно, хорошо для сценариев администратора, но не для производственного кода) - т.е.

-- disable
alter table ChildTableName nocheck constraint ForeignKeyName

-- enable
alter table ChildTableName check constraint ForeignKeyName

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

Вот полный пример; Вы можете посмотреть на план запроса двух DELETE операций ... (в идеале в отрыве от остальной части кода):

create table parent (id int  primary key)
create table child (id int  primary key, pid int)
alter table child add constraint fk_parent foreign key (pid)
    references parent (id)

insert parent values (1)
insert parent values (2)
insert child values (1,1)
insert child values (2,1)

-- ******************* THIS ONE CHECKS THE FOREIGN KEY
delete from parent
where not exists (select 1 from child where pid = parent.id)

-- reset
delete from child
delete from parent
insert parent values (1)
insert parent values (2)
insert child values (1,1)
insert child values (2,1)

-- re-run with check disabled
alter table child nocheck constraint fk_parent

-- ******************* THIS ONE DOESN'T CHECK THE FOREIGN KEY    
delete from parent
where not exists (select 1 from child where pid = parent.id)

-- re-enable
alter table child check constraint fk_parent

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

1 голос
/ 06 июля 2009

Вы можете создать индексированное представление выбранного предложения:

SELECT key FROM table WHERE table.key = centretable.key

Индексированное представление - это физическая копия данных, поэтому проверка будет очень быстрой.

У вас есть накладные расходы на обновление представления, поэтому вам необходимо проверить это в соответствии с вашим шаблоном использования.

0 голосов
/ 06 июля 2009

Короткий ответ на ваш вопрос - нет, стандартного ключевого слова RDBMS для удаления master записи, когда все ссылки на внешние ключи на нее исчезают (и, конечно, нет, которые бы учитывали внешние ключи в нескольких таблицы).

Наиболее эффективным вариантом является второй запрос, который выполняется по мере необходимости для удаления из "центра" на основе серии предложений NOT EXISTS () для каждой из таблиц с внешними ключами.

Это основано на двух утверждениях, которые, на мой взгляд, верны для вашей ситуации:

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

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

0 голосов
/ 06 июля 2009

Я нашел одну статью, в которой обсуждается использование внешнего соединения в удалении: http://www.bennadel.com/blog/939-Using-A-SQL-JOIN-In-A-SQL-DELETE-Statement-Thanks-Pinal-Dave-.htm

Я надеюсь, что это работает для вас!

0 голосов
/ 29 июня 2009

Если вы указали внешний ключ в качестве ограничения при создании таблицы в базе данных, вы можете указать базе данных, что делать в случае удаления, установив правило удаления. Это правило определяет, что происходит, если пользователь пытается удалить строку с данными, которые связаны с отношением внешнего ключа. Параметр «Бездействия» сообщает пользователю, что удаление не разрешено, а УДАЛЕНИЕ отменено. Подобная реализация помешает вам проверить его самостоятельно перед удалением, и, таким образом, может рассматриваться как некая попытка. Ну, по крайней мере, так работает в MS SQL. http://msdn.microsoft.com/en-us/library/ms177288.aspx

0 голосов
/ 29 июня 2009

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

SELECT Key, ...
INTO #ToDelete
FROM Table T
WHERE ...

Тогда как то так

...
LEFT OUTER JOIN #ToDelete D
ON T.Key=D.Key
WHERE D.Key IS NULL

DROP #ToDelete
...