SQL-ревью: сколько строк затронет этот UPDATE

В ревью PR с миграцией цен часто встречается запрос вида:

UPDATE products SET price = price * 1.1 WHERE category_id IN ( SELECT id FROM categories WHERE parent_id = 42 );

Вопрос на собеседовании звучит просто: сколько строк затронет этот UPDATE. Правильный ответ — «не знаю без EXPLAIN», и это не уклонение, а единственно честная позиция.

Подзапрос может вернуть ноль строк, если parent_id = 42 не существует — тогда IN () не затронет ничего. Может вернуть миллион категорий, если иерархия устроена не так, как думает автор миграции. На проде это означает: либо миграция ничего не сделала и никто не заметил, либо она перезаписала половину таблицы ценой долгой блокировки строк.

Вторая часть вопроса — что будет с конкурентными чтениями на время выполнения этого UPDATE. В PostgreSQL он берёт ROW EXCLUSIVE на каждую затронутую строку, и если их миллион — транзакция может идти минутами, удерживая блокировки.

На собеседовании часто просят переписать это в безопасную форму: сначала SELECT count(*) с тем же условием, чтобы оценить масштаб, потом батчами по id с LIMIT, а не одним огромным UPDATE. Ещё спрашивают про идемпотентность миграции — что будет, если она упадёт на середине и запустится повторно.

Миграция без оценки количества строк — это не быстрый UPDATE, а неизвестная по времени блокировка на проде.

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки