Если маловероятно, что неактивные строки, которые не связаны, станут связанными, вы можете запустить (или даже динамически построить на основе метаданных внешнего ключа):
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