Удалить все записи, которые не имеют ограничений внешнего ключа - PullRequest
5 голосов
/ 07 мая 2010

У меня есть таблица SQL 2005 с миллионами строк, которую пользователи посещают днем ​​и ночью. На эту таблицу ссылаются около 20 других таблиц, которые имеют ограничения внешнего ключа. То, что мне нужно делать на регулярной основе, - это удалить все записи из этой таблицы, где для поля «Active» установлено значение false И в других дочерних таблицах нет никаких записей, ссылающихся на родительскую запись. Каков наиболее эффективный способ сделать это, если не попытаться удалить каждый из них по очереди и позволить ему вызвать ошибки SQL для тех, кто нарушает ограничения? Также нельзя отключить ограничения, и я не могу вызывать блокировки родительской таблицы в течение значительного периода времени.

Ответы [ 3 ]

7 голосов
/ 07 мая 2010

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

SELECT k.*
FROM k WITH(NOLOCK)
WHERE k.Active = 0
AND NOT EXISTS (SELECT * FROM f_1 WITH(NOLOCK) WHERE f_1.fk = k.pk)
AND NOT EXISTS (SELECT * FROM f_2 WITH(NOLOCK) WHERE f_2.fk = k.pk)
...
AND NOT EXISTS (SELECT * FROM f_n WITH(NOLOCK) WHERE f_n.fk = k.pk)

И вы можете легко превратить его в УДАЛИТЬ. Но большое удаление может содержать много блокировок, поэтому вы можете поместить это в таблицу, а затем удалить в пакетном режиме - пакет не должен завершиться ошибкой, если запись не была связана.

Чтобы это было эффективным, вам действительно нужно иметь индексы для столбцов FK в связанных таблицах.

Вы также можете сделать это с левыми объединениями, но тогда вам (иногда) придется отказаться от DISTINCT или GROUP BY, и план выполнения обычно не лучше и не так благоприятен для генерации кода:

SELECT k.*
FROM k WITH(NOLOCK)
LEFT JOIN f_1 WITH(NOLOCK) ON f_1.fk = k.pk
LEFT JOIN f_2 WITH(NOLOCK) ON f_2.fk = k.pk
...
LEFT JOIN f_n WITH(NOLOCK) ON f_n.fk = k.pk
WHERE k.Active = 0
    AND f_1.fk IS NULL
    AND f_2.fk IS NULL
    ...
    AND f_n.fk IS NULL
4 голосов
/ 07 мая 2010

Допустим, у нас есть родительская таблица с именем Parent, которая имеет поле «id» любого типа и поле «Active» типа bit. У нас также есть вторая таблица Child с его собственным полем "id" и полем "fk", которое является ссылкой на поле "id" таблицы Parent. Тогда вы можете использовать следующее утверждение:

DELETE Parent
FROM Parent AS p LEFT OUTER JOIN Child AS c ON p.id=c.fk
WHERE c.id IS NULL AND p.Active=0
0 голосов
/ 07 мая 2010

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

Проверьте здесь для внешних соединений: http://en.wikipedia.org/wiki/Join_%28SQL%29#Left_outer_join

Вы должны также написать триггеры, чтобы сделать все это для вас, когда запись удалена или установлена ​​на false и т. Д.

...