Rename 100_backfill_kn_price_per_m2.sql → 120_backfill_kn_price_per_m2.sql to
avoid collision with three existing 100_* files on main (max was 119).
Cast median_ppm2 to ::numeric in Pass 3 so ROUND(double precision, int) resolves
to ROUND(numeric, int) — the double precision overload does not exist in PostgreSQL.
756 977 NULL-status rows in domrf_kn_flats imported from historical
DOM.RF snapshots carry total_area but no price_rub, so the scraper's
price_rub/total_area derivation never fires for them. This left 5/8 EKB
districts at 0% price_per_m2 coverage and blocked the
Limit_District_Median_Partial analytics limitation.
Migration 100 adds a three-pass idempotent backfill:
1. price_rub / total_area (sanity net; 0 rows in practice)
2. domrf_kn_sale_graph.price_avg (type='apartments', latest snapshot)
via obj_id join — fills 453 606 rows
3. peer median from free/sold/booked flats in the same obj_id — fills
620 more rows
After backfill: 61.5% overall coverage (up from 3.7%), all 8 EKB
districts get a non-NULL median (7/8 districts reach ≥ 60%).
Also tightens the UPSERT: price_per_m2 now uses COALESCE so re-scraping
NULL-status flats no longer overwrites a previously backfilled value.