⚡Каскадное удаление за один запрос
Нет, речь не про on delete cascade
Во многих случаях мы не хотим использовать внешние ключи с каскадным удалением, как минимум потому что они существенно повышают цену ошибки - когда случайно удаляется одна сущность и каскадно пол базы
Но как тогда быть, если у нас достаточно глубокая иерархия сущностей, например
a <- b <- c
и хочется по a_id удалить все связанные b и c?
Наивное решение:
- поселектить из b по a_id
- удалить записи из c по полученным b_id
- удалить записи из b
- удалить записи из a
В итоге имеем 4 раудтрипа до базы и сложную логику, которая с ростом иерархии будет выглядить еще более громоздкой
Решение через CTE:
Почему это работает? CTE трактуется как единый стейтмент, соответственно все проверки констрейнтов будут осуществляться только после его полного выполнения
Это нам позволяет удалить все связанные с таблицей a сущности за один раундтрип до базы, и при необходимости легко масштабировать этот подход, просто добавляя новые строки в CTE, если иерархия вырастет
Ставьте 🔥 на этот пост, если нужен рассказ про еще один интересный вариант применения CTE в контексте внешних ключей
Нет, речь не про on delete cascade
Во многих случаях мы не хотим использовать внешние ключи с каскадным удалением, как минимум потому что они существенно повышают цену ошибки - когда случайно удаляется одна сущность и каскадно пол базы
Но как тогда быть, если у нас достаточно глубокая иерархия сущностей, например
a <- b <- c
и хочется по a_id удалить все связанные b и c?
create table a (
id bigserial not null primary key
);
create table b (
id bigserial not null primary key,
a_id bigint not null references a (id)
);
create table c (
id bigserial not null primary key,
b_id bigint not null references b (id)
);
Наивное решение:
- поселектить из b по a_id
- удалить записи из c по полученным b_id
- удалить записи из b
- удалить записи из a
В итоге имеем 4 раудтрипа до базы и сложную логику, которая с ростом иерархии будет выглядить еще более громоздкой
Решение через CTE:
with a_deleted as (
delete from a where id = <a_id> returning *
), b_deleted as (
delete from b where a_id in (select id from a_deleted) returning *
) delete from c where b_id in (select id from b_deleted)
Почему это работает? CTE трактуется как единый стейтмент, соответственно все проверки констрейнтов будут осуществляться только после его полного выполнения
Это нам позволяет удалить все связанные с таблицей a сущности за один раундтрип до базы, и при необходимости легко масштабировать этот подход, просто добавляя новые строки в CTE, если иерархия вырастет
Ставьте 🔥 на этот пост, если нужен рассказ про еще один интересный вариант применения CTE в контексте внешних ключей