Короткий ответ — сначала нужно удалить дочерние записи (в t2), потом родительские (в t1). Это нужно потому, что FK запрещает удалять запись, на которую есть ссылки. Ниже — варианты и рекомендации с примерами для разных ситуаций и СУБД.
1) Простая операция для одного значения (в транзакции)
- Общая логика: BEGIN; DELETE FROM t2 WHERE c22 = :id; DELETE FROM t1 WHERE c11 = :id; COMMIT;
Postgres / MySQL / SQL Server (пример):
BEGIN;
DELETE FROM t2 WHERE c22 = 'some_key';
DELETE FROM t1 WHERE c11 = 'some_key';
COMMIT;
2) Удалить по критерию на родительской таблице (например date < ...)
- Удаляем дочерние строки, которые ссылаются на родителей, затем удаляем родителей.
Postgres:
BEGIN;
DELETE FROM t2 USING t1 WHERE t2.c22 = t1.c11 AND t1.date < '2024-01-01';
DELETE FROM t1 WHERE date < '2024-01-01';
COMMIT;
MySQL:
START TRANSACTION;
DELETE t2 FROM t2 JOIN t1 ON t2.c22 = t1.c11 WHERE t1.date < '2024-01-01';
DELETE FROM t1 WHERE date < '2024-01-01';
COMMIT;
SQL Server:
BEGIN TRANSACTION;
DELETE t2 FROM t2 INNER JOIN t1 ON t2.c22 = t1.c11 WHERE t1.date < '2024-01-01';
DELETE FROM t1 WHERE date < '2024-01-01';
COMMIT;
Альтернативно (портативно), сначала выбрать id родителей, затем удалить по IN/EXISTS:
BEGIN;
DELETE FROM t2 WHERE c22 IN (SELECT c11 FROM t1 WHERE date < '2024-01-01');
DELETE FROM t1 WHERE date < '2024-01-01';
COMMIT;
3) Удаление партиями (для больших объёмов, чтобы не держать долгие блокировки)
- Удалять малыми порциями в цикле (batch), пока не удалится всё.
Пример (SQL Server-подобный синтаксис):
WHILE 1=1
BEGIN
DELETE TOP (1000) t2
FROM t2 JOIN t1 ON t2.c22 = t1.c11
WHERE t1.date < '2024-01-01';
IF @@ROWCOUNT = 0 BREAK;
END
-- затем тоже партиями удалить из t1:
WHILE 1=1
BEGIN
DELETE TOP (1000) FROM t1 WHERE date < '2024-01-01';
IF @@ROWCOUNT = 0 BREAK;
END
4) Если можно менять схему (рекомендуется для долгосрочного решения)
- Добавить ON DELETE CASCADE на FK (или ON DELETE SET NULL), тогда СУБД сама удалит дочерние строки.
Пример (Postgres/MySQL, нужно указать имя ограничения):
ALTER TABLE t2 DROP CONSTRAINT fk_t2_t1; -- найти имя ограничения предварительно
ALTER TABLE t2 ADD CONSTRAINT fk_t2_t1 FOREIGN KEY (c22) REFERENCES t1(c11) ON DELETE CASCADE;
Учтите, что изменение схемы — операция, которую надо тестировать и делать с бэкапом.
5) Что не рекомендуется и когда возможно:
- Временно отключать FK — опасно (можно нарушить целостность), требует последующей проверки.
- Если FK создан как DEFERRABLE (Postgres), можно отложить проверку до конца транзакции: SET CONSTRAINTS fk_name DEFERRED; тогда можно удалить/вставить в произвольном порядке в пределах транзакции. Но это работает только если constraint был создан как DEFERRABLE.
6) Полезные команды для диагностики
- Найти имя FK (пример для PostgreSQL / MySQL / SQL Server см. документацию или information_schema/pg_catalog).
- Перед удалением — сделать бэкап и проверить, какие строки будут затронуты:
SELECT * FROM t1 WHERE date < '...';
SELECT * FROM t2 WHERE c22 IN (SELECT c11 FROM t1 WHERE date < '...');
Резюме:
- Правильный порядок — сначала дочерние строки (t2), потом родители (t1).
- Выполняйте в транзакции, либо партиями для больших объёмов.
- Если это регулярная операция, стоит рассмотреть изменение FK на ON DELETE CASCADE или ON DELETE SET NULL (после тестирования и бэкапа).