B1: дефолт months_window 12→24, расширение fallback 24→27 (max архив). 2024Q1+Q2 теперь реально влияют на recommend_mix (raw 784→3815 сделок для Академического, ×4.9). B2: per-bucket elasticity через MODE по семантике (is_studio+rooms), а не PERCENTILE_CONT(0.5) по total_area. ЖК с mix studios+3к больше не попадают в bucket "1-к" как среднее. Для Академического: 1-к получает 208 точек регрессии (vs 118), slope -1.17. B3: share_window_months и elasticity_window_months в API scope — прозрачность для UI tooltip про рассинхрон окон (24mo rosreestr vs 36mo sale_graph). B4: cad_buildings.floors при INSERT больше НЕ приводится к INT — NSPD отдаёт диапазоны "1-2", "2-3" которые теряются как NULL. Хранится как TEXT, аналитика делает CAST через regex. R1: data/sql/66_indexes_recommend.sql — 4 индекса для горячего пути recommend_mix (sale_graph composite, kn_objects partial, kn_flats partial, cad_buildings partial). Применены в проде через MCP. R3: ON CONFLICT в insert_buildings теперь обновляет ВСЕ изменяемые поля (purpose, address, floors, geom, registration_date) — раньше при re-scrape апдейтились только cost/area/year, остальное игнорилось.
49 lines
3.1 KiB
SQL
49 lines
3.1 KiB
SQL
-- Индексы под горячий путь recommend_mix (analytics_queries.py:recommend_mix).
|
||
--
|
||
-- Без них типичный POST /analytics/recommend/mix делает 3-5 seq-scan'ов на
|
||
-- 5М-строчных таблицах и тратит 1-2 сек. Цель — <500мс.
|
||
--
|
||
-- Идемпотентно через CREATE INDEX IF NOT EXISTS.
|
||
-- CONCURRENTLY НЕ используется (psql сессия с несколькими операциями) —
|
||
-- если будет блок-issue в проде, применять каждый CREATE отдельно.
|
||
|
||
-- 1) sale_graph: JOIN obj_id × type='apartments' × report_month
|
||
-- Используется в _elasticity_coef и _elasticity_per_bucket_coef.
|
||
-- Текущие индексы: (obj_id, snapshot_date), (report_month, type) — но не
|
||
-- покрывают composite seek по obj_id + type + report_month range.
|
||
CREATE INDEX IF NOT EXISTS sg_obj_type_month_idx
|
||
ON domrf_kn_sale_graph (obj_id, type, report_month DESC);
|
||
|
||
-- 2) Active competitors lookup: (district_name, obj_class, site_status)
|
||
-- Используется в _active_competitors_count, который вызывается до 3 раз
|
||
-- (каскадный fallback district+class → district → region) на каждый запрос.
|
||
-- Partial index по site_status='Строящиеся' — это то, что мы реально ищем.
|
||
CREATE INDEX IF NOT EXISTS obj_district_class_active_idx
|
||
ON domrf_kn_objects (district_name, obj_class, region_cd)
|
||
WHERE site_status = 'Строящиеся';
|
||
|
||
-- 3) Per-bucket flats lookup: (obj_id, total_area)
|
||
-- Используется в _elasticity_per_bucket_coef для группировки sale_graph
|
||
-- по «доминирующему bucket» каждого ЖК. Partial index по area-диапазону
|
||
-- который мы реально считаем (15..200 м²).
|
||
CREATE INDEX IF NOT EXISTS flats_obj_area_idx
|
||
ON domrf_kn_flats (obj_id, total_area)
|
||
WHERE total_area BETWEEN 15 AND 200;
|
||
|
||
-- 4) Cadastre baseline: (cost_value, area) — partial для строений
|
||
-- с заполненными ценой и площадью. Используется в _district_cadastre_baseline
|
||
-- + view v_zk_rosreestr_velocity. Также без этого индекса при PERCENTILE_CONT
|
||
-- по cost_value/area делается seq-scan на 10K строк × spatial-join.
|
||
CREATE INDEX IF NOT EXISTS buildings_cost_area_idx
|
||
ON cad_buildings (cost_value, area)
|
||
WHERE cost_value IS NOT NULL AND area IS NOT NULL;
|
||
|
||
-- Комментарии для будущей навигации.
|
||
COMMENT ON INDEX sg_obj_type_month_idx IS
|
||
'Hot path: JOIN domrf_kn_sale_graph по obj_id+type для regression в _elasticity_*.';
|
||
COMMENT ON INDEX obj_district_class_active_idx IS
|
||
'Hot path: _active_competitors_count + recommend_mix scope queries (Partial: site_status=Строящиеся).';
|
||
COMMENT ON INDEX flats_obj_area_idx IS
|
||
'Hot path: per-bucket median total_area для _elasticity_per_bucket_coef.';
|
||
COMMENT ON INDEX buildings_cost_area_idx IS
|
||
'Hot path: cadastre median ₽/м² в _district_cadastre_baseline.';
|