gendesign/tradein-mvp/backend/data/sql/294_dtp_incidents.sql
lekss361 50f0674977
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m9s
Deploy Trade-In / build-backend (push) Successful in 1m54s
Deploy Trade-In / deploy (push) Successful in 1m51s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
feat(tradein): слой ДТП из dtp-stat.ru в PostGIS + радиусные агрегаты (#3428)
2026-09-08 22:10:56 +00:00

79 lines
5.7 KiB
PL/PgSQL
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.

-- 294_dtp_incidents.sql
-- ДТП (dtp-stat.ru, первоисточник — открытые данные ГИБДД), Свердловская обл. (#3410).
--
-- WHY: слой ДТП нужен как радиусный "фактор безопасности" района (по образцу
-- osm_poi_ekb_local / 169_osm_poi_ekb_local.sql — та же архитектура: пустая таблица +
-- индексы на деплое, наполнение отдельным рефреш-джобом, никакого чтения внешнего
-- источника при прогоне миграций).
--
-- ИСТОЧНИК: https://dtp-stat.ru/media/opendata/sverdlovskaia-oblast.geojson.zip — открытый
-- агрегатор данных ГИБДД, без auth. Дамп заморожен (Last-Modified 26.02.2026 на момент
-- проверки 08.09.2026) — см. докстринг app/services/dtp_stat_loader.py, пустая дельта на
-- рефреше это НЕ баг, а факт источника.
--
-- ПДн (важно): properties.vehicles[].participants[] источника содержит пол/роль/нарушения
-- физлиц-участников ДТП. Эта таблица их СОЗНАТЕЛЬНО НЕ хранит — ни колонкой, ни в jsonb.
-- Загрузчик (dtp_stat_loader.py) участников/транспорт из парсинга просто не извлекает.
-- Берём только агрегаты (dead/injured count) и не-персональные атрибуты происшествия.
--
-- ИНДЕКСЫ (урок 270_houses_geog_gist_idx.sql / 134_listings_geom_geography_gist.sql): весь
-- код МЕРЫ ходит радиусными запросами через ST_DWithin(geom::geography, …) — обычный GIST
-- по geometry планировщик под такой предикат не берёт (прод-замер houses: 70.7 мс на
-- bitmap-фильтр всех строк; после функционального индекса по geography — 38 мс на listings).
-- Поэтому сразу оба индекса: geometry (на будущее/для прочих операторов) и функциональный
-- по (geom::geography) — под фактический предикат dtp_index.py.
--
-- Идемпотентно: CREATE TABLE/INDEX IF NOT EXISTS. Безопасно перезапускать.
BEGIN;
SET LOCAL lock_timeout = '5s';
CREATE TABLE IF NOT EXISTS dtp_incidents (
id bigserial PRIMARY KEY,
source_id text UNIQUE,
dtp_at timestamptz,
category text,
severity text,
dead integer,
injured integer,
address text,
light text,
weather text[],
nearby text[],
lat double precision,
lon double precision,
geom geometry(Point, 4326),
loaded_at timestamptz NOT NULL DEFAULT now()
);
COMMENT ON TABLE dtp_incidents IS
'ДТП по Свердловской обл. (dtp-stat.ru, первоисточник — открытые данные ГИБДД, #3410). '
'Пишет app/tasks/dtp_stat_refresh.py (TRUNCATE+INSERT, scheduler source=dtp_stat_refresh, '
'seed 293, enabled=false — включение вручную). Читает app/services/dtp_index.py — '
'радиусные агрегаты (число ДТП/тяжёлых/погибших/раненых) для карточки района. '
'ПДн участников (vehicles/participants: пол, роль, нарушения физлиц) осознанно НЕ '
'хранятся — ни колонкой, ни в jsonb; источник даёт их построчно на транспорт/участника, '
'загрузчик их не извлекает вовсе.';
COMMENT ON COLUMN dtp_incidents.source_id IS 'properties.id из dtp-stat GeoJSON — ключ идемпотентности (UNIQUE).';
COMMENT ON COLUMN dtp_incidents.dtp_at IS 'properties.datetime, распарсено в timestamptz.';
COMMENT ON COLUMN dtp_incidents.category IS 'properties.category — тип ДТП ("Столкновение" и т.п.).';
COMMENT ON COLUMN dtp_incidents.severity IS 'properties.severity — тяжесть ("Тяжёлый"/"Лёгкий"/…).';
COMMENT ON COLUMN dtp_incidents.dead IS 'properties.dead_count — погибшие, агрегат, без ФИО/пола участников.';
COMMENT ON COLUMN dtp_incidents.injured IS 'properties.injured_count — раненые, агрегат.';
COMMENT ON COLUMN dtp_incidents.weather IS 'properties.weather — список погодных условий на момент ДТП.';
COMMENT ON COLUMN dtp_incidents.nearby IS 'properties.nearby — список объектов УДС/притяжения рядом с местом ДТП.';
COMMENT ON COLUMN dtp_incidents.geom IS 'ST_SetSRID(ST_MakePoint(lon, lat), 4326) — строится явно в загрузчике, без триггера (как osm_poi_ekb_local).';
-- GIST по geometry — на будущее / прочие geometry-операторы.
CREATE INDEX IF NOT EXISTS dtp_incidents_geom_idx
ON dtp_incidents USING GIST (geom);
-- Функциональный GIST по (geom::geography) — под фактический предикат
-- ST_DWithin(geom::geography, …) в app/services/dtp_index.py (см. WHY выше).
CREATE INDEX IF NOT EXISTS dtp_incidents_geog_idx
ON dtp_incidents USING GIST ((geom::geography));
CREATE INDEX IF NOT EXISTS dtp_incidents_dtp_at_idx
ON dtp_incidents (dtp_at);
COMMIT;