-- Расширения схемы для регулярного парсинга наш.дом.рф /сервисы/api/kn/{flats,objects}. -- См. план C:\Users\user\.claude\plans\crispy-waddling-swing.md. -- ── Версионирование строк через UNIQUE (id, snapshot_date) ──────────────── -- Текущий PK на flats.id и objects.obj_id мешает сохранять несколько срезов -- одного и того же ЖК/квартиры. Меняем на UNIQUE по (id, snapshot_date) -- чтобы UPSERT идемпотентно записывал свежий snapshot. ALTER TABLE domrf_kn_flats DROP CONSTRAINT IF EXISTS domrf_kn_flats_pkey; ALTER TABLE domrf_kn_flats ADD CONSTRAINT uq_kn_flats_id_snap UNIQUE (id, snapshot_date); ALTER TABLE domrf_kn_objects DROP CONSTRAINT IF EXISTS domrf_kn_objects_pkey; ALTER TABLE domrf_kn_objects ADD CONSTRAINT uq_kn_objects_id_snap UNIQUE (obj_id, snapshot_date); -- ── Backfill region_cd для Антоновых flats где obj_id матчится с Свердл ── UPDATE domrf_kn_flats f SET region_cd = 66 FROM domrf_kn_objects o WHERE f.obj_id = o.obj_id AND o.region_cd = 66 AND f.region_cd IS NULL; -- ── Композитные индексы для выборок последнего среза ────────────────────── CREATE INDEX IF NOT EXISTS idx_kn_flats_region_snap ON domrf_kn_flats (region_cd, snapshot_date); CREATE INDEX IF NOT EXISTS idx_kn_objects_region_snap ON domrf_kn_objects (region_cd, snapshot_date); -- ── Журнал прогонов скрейпера ───────────────────────────────────────────── CREATE TABLE IF NOT EXISTS kn_scrape_runs ( run_id BIGSERIAL PRIMARY KEY, started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), finished_at TIMESTAMPTZ, region_codes INT[] NOT NULL, developer_ids TEXT[], objects_count INT, flats_count INT, requests_count INT, status TEXT NOT NULL DEFAULT 'running', -- running | done | failed error TEXT, snapshot_date DATE NOT NULL DEFAULT CURRENT_DATE ); CREATE INDEX IF NOT EXISTS idx_kn_runs_status ON kn_scrape_runs (status, started_at DESC); COMMENT ON TABLE kn_scrape_runs IS 'Журнал запусков парсера наш.дом.рф kn-API. Один прогон = один (regions, developers) sweep.';