gendesign/data/sql/40_relations.sql
2026-04-26 22:30:52 +03:00

287 lines
16 KiB
SQL
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.

-- Foreign-key constraints + helper VIEWs across DOM.RF + Rosreestr tables.
-- Idempotent: bootstraps missing dimension rows first, then ALTER TABLE ... ADD CONSTRAINT IF NOT EXISTS.
-- ─────────────────────────────────────────────────────────────────────────
-- 1. BOOTSTRAP DIMENSION TABLES
-- ─────────────────────────────────────────────────────────────────────────
-- Add row 0 = РФ to domrf_regions (used as "all-Russia" placeholder by some tables)
INSERT INTO domrf_regions (region_id, region_name, federal_district)
VALUES (0, 'Российская Федерация', '_RF')
ON CONFLICT (region_id) DO NOTHING;
-- Backfill domrf_snapshots from every snapshot_date currently used in children
INSERT INTO domrf_snapshots (snapshot_date, notes)
SELECT DISTINCT s, 'auto-backfilled by 40_relations.sql' FROM (
SELECT snapshot_date AS s FROM domrf_developers_full
UNION SELECT snapshot_date FROM domrf_escrow_banks
UNION SELECT snapshot_date FROM domrf_guaranty_regions
UNION SELECT snapshot_date FROM domrf_housing_summary
UNION SELECT snapshot_date FROM domrf_planned_commissioning
UNION SELECT snapshot_date FROM domrf_sold_out
UNION SELECT snapshot_date FROM domrf_sold_out_by_year
UNION SELECT snapshot_date FROM domrf_sold_out_by_progress
UNION SELECT snapshot_date FROM domrf_mortgage_rates
UNION SELECT snapshot_date FROM domrf_region_aggregates
UNION SELECT snapshot_date FROM domrf_flat_area_distribution
UNION SELECT snapshot_date FROM domrf_developer_aggregates
UNION SELECT snapshot_date FROM domrf_launch_top
UNION SELECT snapshot_date FROM domrf_launch_obj_class
UNION SELECT snapshot_date FROM domrf_launch_monthly
UNION SELECT snapshot_date FROM domrf_sold_ready_index
UNION SELECT snapshot_date FROM domrf_sold_ready_breakdown
UNION SELECT snapshot_date FROM domrf_sold_ready_dynamics
UNION SELECT snapshot_date FROM domrf_project_finance
UNION SELECT snapshot_date FROM domrf_commissioning
UNION SELECT snapshot_date FROM domrf_mortgage_dashboard
UNION SELECT snapshot_date FROM domrf_mortgage_details
) AS dates
ON CONFLICT (snapshot_date) DO NOTHING;
-- Helper to add a constraint only if it does not already exist
-- (PostgreSQL ≥9.6: do via DO block; idempotent)
DO $$
DECLARE
rec RECORD;
BEGIN
-- ──────────────────────────────────────────────────────────────────────
-- 2. SNAPSHOT FKs (every domrf_* with snapshot_date column)
-- ──────────────────────────────────────────────────────────────────────
FOR rec IN SELECT * FROM (VALUES
('domrf_developers_full', 'fk_snap_developers_full'),
('domrf_escrow_banks', 'fk_snap_escrow_banks'),
('domrf_guaranty_regions', 'fk_snap_guaranty_regions'),
('domrf_housing_summary', 'fk_snap_housing_summary'),
('domrf_planned_commissioning', 'fk_snap_planned_commissioning'),
('domrf_sold_out', 'fk_snap_sold_out'),
('domrf_sold_out_by_year', 'fk_snap_sold_out_by_year'),
('domrf_sold_out_by_progress', 'fk_snap_sold_out_by_progress'),
('domrf_mortgage_rates', 'fk_snap_mortgage_rates'),
('domrf_region_aggregates', 'fk_snap_region_aggregates'),
('domrf_flat_area_distribution', 'fk_snap_flat_area_distribution'),
('domrf_developer_aggregates', 'fk_snap_developer_aggregates'),
('domrf_launch_top', 'fk_snap_launch_top'),
('domrf_launch_obj_class', 'fk_snap_launch_obj_class'),
('domrf_launch_monthly', 'fk_snap_launch_monthly'),
('domrf_sold_ready_index', 'fk_snap_sold_ready_index'),
('domrf_sold_ready_breakdown', 'fk_snap_sold_ready_breakdown'),
('domrf_sold_ready_dynamics', 'fk_snap_sold_ready_dynamics'),
('domrf_project_finance', 'fk_snap_project_finance'),
('domrf_commissioning', 'fk_snap_commissioning'),
('domrf_mortgage_dashboard', 'fk_snap_mortgage_dashboard'),
('domrf_mortgage_details', 'fk_snap_mortgage_details'),
('domrf_raw_endpoints', 'fk_snap_raw_endpoints')
) AS t(tbl, cn)
LOOP
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = rec.cn
) THEN
EXECUTE format(
'ALTER TABLE %I ADD CONSTRAINT %I FOREIGN KEY (snapshot_date) REFERENCES domrf_snapshots(snapshot_date) ON UPDATE CASCADE',
rec.tbl, rec.cn
);
END IF;
END LOOP;
-- ──────────────────────────────────────────────────────────────────────
-- 3. REGION FKs (domrf_region_aggregates / flat_area_distribution / developer_aggregates → domrf_regions)
-- ──────────────────────────────────────────────────────────────────────
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_region_aggregates_region') THEN
ALTER TABLE domrf_region_aggregates
ADD CONSTRAINT fk_region_aggregates_region
FOREIGN KEY (region_id) REFERENCES domrf_regions(region_id) ON UPDATE CASCADE;
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_flat_area_region') THEN
ALTER TABLE domrf_flat_area_distribution
ADD CONSTRAINT fk_flat_area_region
FOREIGN KEY (region_id) REFERENCES domrf_regions(region_id) ON UPDATE CASCADE;
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_developer_agg_region') THEN
ALTER TABLE domrf_developer_aggregates
ADD CONSTRAINT fk_developer_agg_region
FOREIGN KEY (region_id) REFERENCES domrf_regions(region_id) ON UPDATE CASCADE;
END IF;
-- ──────────────────────────────────────────────────────────────────────
-- 4. DEVELOPER FK (domrf_developer_aggregates → domrf_developers)
-- ──────────────────────────────────────────────────────────────────────
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_developer_agg_developer') THEN
ALTER TABLE domrf_developer_aggregates
ADD CONSTRAINT fk_developer_agg_developer
FOREIGN KEY (developer_id) REFERENCES domrf_developers(developer_id) ON UPDATE CASCADE;
END IF;
END $$;
-- ─────────────────────────────────────────────────────────────────────────
-- 5. HELPER VIEWS
-- ─────────────────────────────────────────────────────────────────────────
-- v_region_master: одна строка на регион — все ключевые метрики на последний снапшот
DROP VIEW IF EXISTS v_region_master CASCADE;
CREATE VIEW v_region_master AS
WITH latest AS (SELECT MAX(snapshot_date) AS d FROM domrf_snapshots)
SELECT
r.region_id,
r.region_name,
r.federal_district,
-- Quartirografia totals (TOTAL row)
rta.flat_count AS total_flats,
rta.area_sqm AS total_area_sqm,
-- Sold-out (DOM.RF PDF data)
so.total_area_th_sqm AS sold_out_total_th_sqm,
so.sold_pct AS sold_out_sold_pct,
so.unsold_pct AS sold_out_unsold_pct,
-- Sold-ready breakdown (Playwright API data)
srb.square_sum AS sold_ready_square,
srb.sold_perc AS sold_ready_sold_pct,
srb.ready_perc AS sold_ready_ready_pct,
srb.sold_ready_perc AS sold_ready_balance_pct,
-- Project finance
pf.rns_cnt AS project_finance_permits,
pf.dev_cnt AS project_finance_developers,
pf.guaranty_escrow_dev_cnt AS project_finance_escrow_devs,
-- Guaranty (XLSX)
g.fz214_developers AS guaranty_fz214_devs,
g.escrow_developers AS guaranty_escrow_devs,
-- Launch (запуски новых)
(SELECT value FROM domrf_launch_top
WHERE dim_type = 'region' AND metric_type = 'app'
AND entity_id = r.region_id::text AND snapshot_date = (SELECT d FROM latest)
LIMIT 1) AS launch_app_2026_sqm,
(SELECT value FROM domrf_launch_top
WHERE dim_type = 'region' AND metric_type = 'rnv'
AND entity_id = r.region_id::text AND snapshot_date = (SELECT d FROM latest)
LIMIT 1) AS launch_rnv_2026_sqm
FROM domrf_regions r
LEFT JOIN domrf_region_aggregates rta
ON rta.region_id = r.region_id
AND rta.room_count_type = 'TOTAL'
AND rta.snapshot_date = (SELECT d FROM latest)
LEFT JOIN domrf_sold_out so
ON so.territory_name = r.region_name
AND so.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_sold_out WHERE territory_name = r.region_name)
LEFT JOIN domrf_sold_ready_breakdown srb
ON srb.entity_key = r.region_name
AND srb.chart_type = 'regionChart'
AND srb.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_sold_ready_breakdown WHERE chart_type='regionChart')
LEFT JOIN domrf_project_finance pf
ON pf.subject_name = r.region_name
AND pf.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_project_finance)
LEFT JOIN domrf_guaranty_regions g
ON g.territory_name = r.region_name
AND g.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_guaranty_regions)
WHERE r.region_id > 0
ORDER BY total_area_sqm DESC NULLS LAST;
COMMENT ON VIEW v_region_master IS
'Один срез по региону: квартирография + распроданность (DOM.РФ + sold_out PDF) + проектное финансирование + запуски/ввод 2026';
-- v_developer_master: профиль застройщика (latest snapshot)
DROP VIEW IF EXISTS v_developer_master CASCADE;
CREATE VIEW v_developer_master AS
WITH latest AS (SELECT MAX(snapshot_date) AS d FROM domrf_snapshots)
SELECT
d.developer_id,
d.developer_name,
-- Group-of-companies aggregates (from JSON dashboard)
dat.flat_count AS group_flat_count,
dat.area_sqm AS group_area_sqm,
-- Per-юрлицо реестр (XLSX)
df.area_thousand_sqm AS legal_area_th_sqm,
df.permits_count AS legal_permits,
df.houses_count AS legal_houses,
df.flats_count AS legal_flats,
df.market_share_pct AS market_share_pct,
-- Запуски / ввод 2026 (из launch_top, dim=developer)
(SELECT value FROM domrf_launch_top
WHERE dim_type='developer' AND metric_type='app'
AND entity_id = d.developer_id AND snapshot_date = (SELECT d FROM latest)
LIMIT 1) AS launch_app_2026_sqm,
(SELECT value FROM domrf_launch_top
WHERE dim_type='developer' AND metric_type='rnv'
AND entity_id = d.developer_id AND snapshot_date = (SELECT d FROM latest)
LIMIT 1) AS launch_rnv_2026_sqm,
-- Распроданность (devChart)
(SELECT square_sum FROM domrf_sold_ready_breakdown
WHERE chart_type='devChart' AND entity_key = d.developer_name
AND snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_sold_ready_breakdown WHERE chart_type='devChart')
LIMIT 1) AS sold_ready_square,
(SELECT sold_perc FROM domrf_sold_ready_breakdown
WHERE chart_type='devChart' AND entity_key = d.developer_name
AND snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_sold_ready_breakdown WHERE chart_type='devChart')
LIMIT 1) AS sold_ready_sold_pct
FROM domrf_developers d
LEFT JOIN domrf_developer_aggregates dat
ON dat.developer_id = d.developer_id
AND dat.room_count_type = 'TOTAL'
AND dat.snapshot_date = (SELECT d FROM latest)
LEFT JOIN domrf_developers_full df
ON df.developer_name = d.developer_name
AND df.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_developers_full WHERE developer_name = d.developer_name);
COMMENT ON VIEW v_developer_master IS
'Профиль застройщика: реестр + квартирография по комнатностям + запуски/ввод + распроданность';
-- v_rosreestr_market_by_region — срез цен сделок и застройщиков по региону
DROP VIEW IF EXISTS v_rosreestr_market_by_region CASCADE;
CREATE VIEW v_rosreestr_market_by_region AS
SELECT
r.region_id,
r.region_name,
r.federal_district,
rar.period_start_date,
rar.doc_type,
rar.deals_count,
rar.median_price_sqm AS median_rub_sqm,
rar.p10_price_sqm AS p10_rub_sqm,
rar.p90_price_sqm AS p90_rub_sqm
FROM domrf_regions r
JOIN rr_agg_region rar
ON rar.region_code = r.region_id
WHERE rar.realestate_type_code = '002001003000' -- Помещения (квартиры)
ORDER BY r.region_id, rar.period_start_date, rar.doc_type;
COMMENT ON VIEW v_rosreestr_market_by_region IS
'Цены сделок Росреестра по регионам с DOM.РФ-именами регионов (квартиры)';
-- v_market_pulse_rf — РФ-уровень summary с разных источников за last snapshot
DROP VIEW IF EXISTS v_market_pulse_rf CASCADE;
CREATE VIEW v_market_pulse_rf AS
SELECT
-- Quartirografia
(SELECT SUM(area_sqm)/1e6 FROM domrf_region_aggregates
WHERE room_count_type='TOTAL'
AND snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_region_aggregates)) AS total_construction_mln_sqm,
-- Sold-ready
(SELECT square_sum/1e6 FROM domrf_sold_ready_index
ORDER BY snapshot_date DESC LIMIT 1) AS sold_ready_total_mln_sqm,
(SELECT sold_perc FROM domrf_sold_ready_index
ORDER BY snapshot_date DESC LIMIT 1) AS sold_pct,
(SELECT ready_perc FROM domrf_sold_ready_index
ORDER BY snapshot_date DESC LIMIT 1) AS ready_pct,
-- Прайс
(SELECT avg_price_per_sqm FROM domrf_sold_out
WHERE territory_name = 'Российская Федерация'
ORDER BY snapshot_date DESC LIMIT 1) AS rf_avg_price_per_sqm,
-- Ставки
(SELECT primary_rate FROM domrf_mortgage_rates
WHERE bank_name = 'Средневзвешенная'
ORDER BY snapshot_date DESC LIMIT 1) AS market_primary_rate,
(SELECT secondary_rate FROM domrf_mortgage_rates
WHERE bank_name = 'Средневзвешенная'
ORDER BY snapshot_date DESC LIMIT 1) AS market_secondary_rate,
-- Реальные ставки (по выданным кредитам)
(SELECT primary_credit_avg_rate FROM domrf_mortgage_dashboard
ORDER BY snapshot_date DESC LIMIT 1) AS actual_primary_rate,
(SELECT secondary_credit_avg_rate FROM domrf_mortgage_dashboard
ORDER BY snapshot_date DESC LIMIT 1) AS actual_secondary_rate,
-- Объём кредитования
(SELECT total_credit_count FROM domrf_mortgage_dashboard
ORDER BY snapshot_date DESC LIMIT 1) AS total_credit_count,
(SELECT total_credit_amount/1e9 FROM domrf_mortgage_dashboard
ORDER BY snapshot_date DESC LIMIT 1) AS total_credit_amount_bln_rub;
COMMENT ON VIEW v_market_pulse_rf IS
'РФ pulse: объём строительства, распроданность, средняя цена, ставки (рыночные vs реальные), объём ипотеки';