gendesign/data/sql/189_land_reservation_nulls_not_distinct.sql
bot-backend 04f70b8da0
All checks were successful
Deploy / changes (push) Successful in 12s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m17s
Deploy / build-worker (push) Successful in 3m34s
Deploy / deploy (push) Successful in 2m16s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 10s
fix(ptica): land_reservation перестаёт копить дубли — 91% таблицы были копиями (#2464) (#2966)
2026-08-20 10:16:34 +00:00

58 lines
3.8 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 189_land_reservation_nulls_not_distinct.sql
-- #2464 — дедуп land_reservation и UNIQUE NULLS NOT DISTINCT на живой таблице.
--
-- БАГ. `_UPSERT_NO_ACT_SQL` (workers/tasks/izyatie_ocr_ingest.py) заканчивается
-- `ON CONFLICT DO NOTHING`, а единственный подходящий констрейнт —
-- `uq_land_reservation_cad_act UNIQUE (cad_num, act_number)` с обычной NULL-семантикой.
-- В Postgres NULL != NULL, поэтому у записей БЕЗ номера акта конфликт не наступает
-- никогда: `ON CONFLICT DO NOTHING` не срабатывает, и каждый недельный прогон
-- вставляет копию. Docstring таски при этом обещает per-batch дедуп и двухшаговый
-- upsert по (cad_num, doc_url) — ни того, ни другого в коде нет.
--
-- ЗАМЕР ПРОДА 2026-08-20 (до правки):
-- строк всего 297
-- из них с act_number IS NULL 297 (то есть все)
-- групп (cad_num, doc_url) с дублями 27
-- максимум копий в группе 11
-- лишних строк 270 (91% таблицы)
--
-- Проверено, что ключ подходит: ни у одного cad_num нет более одного doc_url
-- (max = 1), то есть NULLS NOT DISTINCT по (cad_num, act_number) НЕ схлопнет
-- разные документы одного участка. Дубли внутри групп — точные копии: по одному
-- различному значению reservation_kind и act_date на группу.
--
-- ЧТО УДАЛЯЕТСЯ. Строки-копии сверх первой (по возрастанию id) в каждой группе
-- (cad_num, act_number) среди act_number IS NULL. Это порождение бага, а не
-- пользовательские данные; таблица — кэш OCR-разбора PDF с сайта, пересобираемый
-- прогоном таски. Первая строка группы (минимальный id) сохраняется целиком.
--
-- ПОЧЕМУ ОТДЕЛЬНОЙ МИГРАЦИЕЙ, а не правкой CREATE TABLE: та же причина, что в
-- м.158 — исходный файл уже в _schema_migrations и на деплое пропускается.
-- Прецеденты NULLS NOT DISTINCT в репо: м.110, м.125, м.140, м.158. Prod = PG16.4.
-- Apply after: 188_regrant_quarter_price_index_fdw.sql
BEGIN;
-- #2752: блокирующий DDL обязан иметь lock_timeout — иначе ALTER TABLE встанет в
-- очередь за чужой сессией и уведёт за собой запросы приложения. Таблица крошечная
-- (297 строк), сам DDL мгновенный; пять секунд — про ОЖИДАНИЕ блокировки, не про
-- работу. Не дождались — миграция падает, а не подвешивает прод.
SET LOCAL lock_timeout = '5s';
-- 1) Дедуп: оставляем строку с минимальным id в каждой группе.
DELETE FROM land_reservation a
USING land_reservation b
WHERE a.act_number IS NULL
AND b.act_number IS NULL
AND a.cad_num = b.cad_num
AND a.id > b.id;
-- 2) Пересоздаём констрейнт с NULL-семантикой, при которой ON CONFLICT матчит.
ALTER TABLE land_reservation
DROP CONSTRAINT IF EXISTS uq_land_reservation_cad_act;
ALTER TABLE land_reservation
ADD CONSTRAINT uq_land_reservation_cad_act
UNIQUE NULLS NOT DISTINCT (cad_num, act_number);
COMMIT;