Demand D1: mv_quarter_price_per_m2 из rosreestr_deals (6.83M ДДУ) #33

Closed
opened 2026-05-11 20:14:34 +00:00 by lekss361 · 0 comments
lekss361 commented 2026-05-11 20:14:34 +00:00 (Migrated from github.com)

Эшелон: 🟠 Demand — «Продастся ли»

Контекст / проблема

В БД лежит 6 830 001 ДДУ в rosreestr_deals (partitioned 2024q1-2026q1). Это ground truth по фактической цене м² новостроек. Сейчас совсем не используется в site-finder.

Текущая median_price берётся из ekb_districts (9 районов на весь город — слишком грубо). Из ДДУ можно построить per-cad_quarter price (≈9700 ячеек, гранулярность улицы).

Что сделать

  1. Создать materialized view mv_quarter_price_per_m2:
    CREATE MATERIALIZED VIEW mv_quarter_price_per_m2 AS
    SELECT
      cad_quarter,
      COUNT(*) AS deals_count,
      PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY price_per_m2) AS p25,
      PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY price_per_m2) AS median,
      PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY price_per_m2) AS p75,
      AVG(price_per_m2) AS mean,
      MAX(deal_date) AS last_deal_date,
      -- временные срезы
      PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '6 months') AS median_6m,
      PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '12 months') AS median_12m,
      PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '24 months') AS median_24m
    FROM rosreestr_deals
    WHERE realestate_type = '002001003000'  -- квартиры новостроек
      AND price_per_m2 BETWEEN 30000 AND 800000  -- outlier filter
    GROUP BY cad_quarter
    HAVING COUNT(*) >= 3;
    
  2. GIST/BTREE index на cad_quarter
  3. Refresh schedule — еженедельный Celery beat task
  4. Backend integration в /analyzedemand.market_price: {p25, median, p75, deals_count, freshness}
  5. UI display: ✕ заменить district median на quarter median + range

Acceptance

  • >7000 cad_quarters в mv (из ≈9700 импортированных)
  • Известный quarter ЕКБ (Центр) median 200K+, дальний район 100-120K
  • Refresh идёт <60 сек (10K квартала × агрегация по 6.83M)
  • analyze возвращает quarter price вместо district median

Owner

database-expert (mv + index + refresh logic)

Effort

1–1.5 дня

Risks / open

  • price_per_m2 в rosreestr_deals нужно проверить — есть ли уже посчитанное поле или считать price/area
  • Outlier фильтр 30K-800K может отсечь некоторые валидные сделки в премиум-зонах — проверить distribution
  • Refresh строится CONCURRENTLY — иначе будет блокировать reads

Dependencies

— (foundation для D5)

**Эшелон:** 🟠 Demand — «Продастся ли» ## Контекст / проблема В БД лежит **6 830 001 ДДУ** в `rosreestr_deals` (partitioned 2024q1-2026q1). Это ground truth по фактической цене м² новостроек. Сейчас **совсем не используется** в site-finder. Текущая median_price берётся из `ekb_districts` (9 районов на весь город — слишком грубо). Из ДДУ можно построить **per-cad_quarter** price (≈9700 ячеек, гранулярность улицы). ## Что сделать 1. Создать materialized view `mv_quarter_price_per_m2`: ```sql CREATE MATERIALIZED VIEW mv_quarter_price_per_m2 AS SELECT cad_quarter, COUNT(*) AS deals_count, PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY price_per_m2) AS p25, PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY price_per_m2) AS median, PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY price_per_m2) AS p75, AVG(price_per_m2) AS mean, MAX(deal_date) AS last_deal_date, -- временные срезы PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '6 months') AS median_6m, PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '12 months') AS median_12m, PERCENTILE_CONT(0.50) FILTER (WHERE deal_date > NOW() - INTERVAL '24 months') AS median_24m FROM rosreestr_deals WHERE realestate_type = '002001003000' -- квартиры новостроек AND price_per_m2 BETWEEN 30000 AND 800000 -- outlier filter GROUP BY cad_quarter HAVING COUNT(*) >= 3; ``` 2. GIST/BTREE index на `cad_quarter` 3. Refresh schedule — еженедельный Celery beat task 4. Backend integration в `/analyze` — `demand.market_price: {p25, median, p75, deals_count, freshness}` 5. UI display: ✕ заменить district median на quarter median + range ## Acceptance - [ ] >7000 cad_quarters в mv (из ≈9700 импортированных) - [ ] Известный quarter ЕКБ (Центр) median 200K+, дальний район 100-120K - [ ] Refresh идёт <60 сек (10K квартала × агрегация по 6.83M) - [ ] analyze возвращает quarter price вместо district median ## Owner `database-expert` (mv + index + refresh logic) ## Effort 1–1.5 дня ## Risks / open - price_per_m2 в `rosreestr_deals` нужно проверить — есть ли уже посчитанное поле или считать `price/area` - Outlier фильтр 30K-800K может отсечь некоторые валидные сделки в премиум-зонах — проверить distribution - Refresh строится **CONCURRENTLY** — иначе будет блокировать reads ## Dependencies — (foundation для D5) ## Links - [SalesTracking_Strategy_Apr25](https://obsidian.gendsgn.ru/) — была обозначена эта возможность
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#33
No description provided.