107 lines
7.8 KiB
PL/PgSQL
107 lines
7.8 KiB
PL/PgSQL
-- Контекст: Issue #42 (Infra I1) — POI saturation per capita. Чтобы считать
|
||
-- обеспеченность инфраструктурой района (школа/детсад/поликлиника на
|
||
-- 1000 чел. целевой когорты vs норматив СП 42.13330), нужна демография
|
||
-- по 8 внутригородским районам ЕКБ.
|
||
--
|
||
-- Что делает:
|
||
-- 1. Заполняет ekb_districts.population (была NULL) точными числами Росстата.
|
||
-- 2. Создаёт таблицу-справочник ekb_district_demographics — насел./площадь/ОКАТО
|
||
-- + ДОКУМЕНТИРОВАННЫЙ источник/дата + ОЦЕНОЧНЫЕ возрастные когорты (помечены).
|
||
--
|
||
-- ИСТОЧНИК населения (источник истины):
|
||
-- Wikipedia «Административно-территориальное деление Екатеринбурга» →
|
||
-- первоисточник «Численность постоянного населения РФ по муниципальным
|
||
-- образованиям на 1 января 2025», Росстат. as_of_date = 2025-01-01.
|
||
--
|
||
-- ВОЗРАСТНЫЕ КОГОРТЫ — ОЦЕНКА, НЕ ФАКТ. На уровне внутригородского района Росстат
|
||
-- публичных половозрастных срезов НЕ даёт. Применены РЕГИОНАЛЬНЫЕ доли (РФ/Свердл.
|
||
-- обл., перепись-2020 / EMISS, порядок величин):
|
||
-- • дошкольники 0-7 ≈ 8.0 % (age_share_preschool)
|
||
-- • школьники 7-18 ≈ 11.5 % (age_share_school)
|
||
-- • пожилые 65+ ≈ 16.0 % (age_share_elderly)
|
||
-- Это ГРУБАЯ оценка для saturation-расчёта (estimate-флаг в API). Реальные
|
||
-- район-уровневые доли потребуют отдельного источника (см. issue #42 risks).
|
||
--
|
||
-- Deploy order: после 56_schema_ekb_districts_geom (FK на ekb_districts) — стоит.
|
||
-- Idempotent: CREATE TABLE IF NOT EXISTS + INSERT … ON CONFLICT DO UPDATE
|
||
-- (re-apply перезаписывает данными из этой миграции — single source).
|
||
-- Deploy: auto-applied by deploy.yml через _schema_migrations tracking (ровно один раз по NN).
|
||
|
||
BEGIN;
|
||
|
||
-- ── 1. Справочник демографии района ──────────────────────────────────────────
|
||
CREATE TABLE IF NOT EXISTS ekb_district_demographics (
|
||
district_name TEXT PRIMARY KEY
|
||
REFERENCES ekb_districts(district_name),
|
||
population INTEGER NOT NULL, -- чел., Росстат 2025-01-01
|
||
area_km2 NUMERIC(8, 2) NOT NULL, -- км²
|
||
okato TEXT, -- код ОКАТО района
|
||
-- Возрастные доли — ОЦЕНКА (региональный fallback), а не район-факт.
|
||
age_share_preschool NUMERIC(5, 4), -- доля 0-7 лет (детсад)
|
||
age_share_school NUMERIC(5, 4), -- доля 7-18 лет (школа)
|
||
age_share_elderly NUMERIC(5, 4), -- доля 65+ лет (поликлиника)
|
||
age_cohorts_estimated BOOLEAN NOT NULL DEFAULT TRUE, -- TRUE = когорты оценочные
|
||
source TEXT NOT NULL, -- человекочитаемый источник
|
||
as_of_date DATE NOT NULL, -- дата актуальности населения
|
||
loaded_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
);
|
||
|
||
COMMENT ON TABLE ekb_district_demographics IS
|
||
'Демография 8 внутригородских районов ЕКБ для POI saturation per capita (#42). '
|
||
'population/area — Росстат 2025-01-01 (факт). Возрастные когорты — ОЦЕНКА '
|
||
'(региональные доли РФ/Свердл.), см. age_cohorts_estimated.';
|
||
COMMENT ON COLUMN ekb_district_demographics.age_cohorts_estimated IS
|
||
'TRUE: age_share_* — оценка региональными долями, НЕ район-факт Росстата (#42).';
|
||
|
||
-- ── 2. Seed: 8 районов (Росстат на 1 января 2025) ────────────────────────────
|
||
-- population/area/okato — ФАКТ. age_share_* — единая региональная оценка (см. шапку).
|
||
INSERT INTO ekb_district_demographics
|
||
(district_name, population, area_km2, okato,
|
||
age_share_preschool, age_share_school, age_share_elderly,
|
||
age_cohorts_estimated, source, as_of_date)
|
||
VALUES
|
||
('Академический', 125000, 46.01, '65401362', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Верх-Исетский', 240822, 219.79, '65401364', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Железнодорожный', 158675, 125.65, '65401368', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Кировский', 220749, 86.25, '65401373', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Ленинский', 222258, 22.19, '65401377', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Октябрьский', 151775, 158.60, '65401380', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Орджоникидзевский', 263820, 99.30, '65401385', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01'),
|
||
('Чкаловский', 286277, 389.81, '65401390', 0.0800, 0.1150, 0.1600, TRUE,
|
||
'Росстат, численность населения МО на 01.01.2025 (via Wikipedia АТД Екатеринбурга)',
|
||
DATE '2025-01-01')
|
||
ON CONFLICT (district_name) DO UPDATE SET
|
||
population = EXCLUDED.population,
|
||
area_km2 = EXCLUDED.area_km2,
|
||
okato = EXCLUDED.okato,
|
||
age_share_preschool = EXCLUDED.age_share_preschool,
|
||
age_share_school = EXCLUDED.age_share_school,
|
||
age_share_elderly = EXCLUDED.age_share_elderly,
|
||
age_cohorts_estimated = EXCLUDED.age_cohorts_estimated,
|
||
source = EXCLUDED.source,
|
||
as_of_date = EXCLUDED.as_of_date,
|
||
loaded_at = now();
|
||
|
||
-- ── 3. Backfill ekb_districts.population (была NULL) — по name-match ──────────
|
||
-- ekb_districts.area_m2 уже есть в схеме (NUMERIC) — конвертируем км² → м².
|
||
UPDATE ekb_districts d
|
||
SET population = s.population,
|
||
area_m2 = COALESCE(d.area_m2, s.area_km2 * 1000000.0)
|
||
FROM ekb_district_demographics s
|
||
WHERE d.district_name = s.district_name;
|
||
|
||
COMMIT;
|