Привет, многоуважаемые читатели Хабра!
Как нынешний аналитик БД с прошлым пятилетним опытом в B2B и B2C сегментах телеком-провайдеров, я хотел бы осветить одну из "болей" абонентов - «архивные» тарифы. Абоненты годами сидят на устаревших и дорогих тарифах, не зная, что в их же доме уже давно доступны более выгодные предложения от конкурентов.
Чтобы решить эту проблему и обеспечить открытость рынка тарифов, я решил объединить свой опыт в телекоме с навыками программирования. Так родилась идея проекта по автоматизированному парсингу тарифов провайдеров.
В первой части цикла статей я расскажу об архитектуре базы данных, которую я спроектировал для этой задачи.
Зачем это нужно?
Миссия проекта — создать инструмент, который автоматически мониторит провайдеров, избавляя пользователей от ручного сравнения и помогая им находить оптимальные условия по тарифу.
Архитектура БД: схема «Звезда» на PostgreSQL

Проектирование я начал со структуры на PostgreSQL, используя схему «Звезда».
Таблица фактов: Таблица
город_провайдерсо связью города с провайдером.Таблицы измерений: Таблицы с информацией о тарифах (
тарифы), городах (города) и провайдерах (провайдеры).
Для удобства дальнейшей работы все первичные и внешние ключи созданы в виде именованных ограничений.
CONSTRAINT ограничение_для_pk_id PRIMARY KEY (id),
CONSTRAINT ограничение_для_fk_id FOREIGN KEY (id) REFERENCES (id),
CONSTRAINT uq_город_провайдер UNIQUE (город_id, провайдер_id) -- уникальность значений для таблицы связей
Управление историчностью и автоматизация
Чтобы отслеживать изменения данных (например, когда провайдер деактивирует старый тариф), в таблицах связей, провайдерах и тарифах ведется историчность изменений с помощью автоматических триггеров.
Каждая сущность (кроме городов) имеет временные метки start_at и end_at для отслеживания актуальности записи.
CREATE TABLE IF NOT EXISTS сущности (
-- ...
start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- для сущности связи
end_at TIMESTAMP DEFAULT NULL, -- NULL значит «действует до сих пор»
is_active BOOLEAN DEFAULT TRUE
);
Функции автоматически управляют этими полями при обновлениях:
CREATE OR REPLACE FUNCTION функция_реагирующая_на_изменения()
RETURNS TRIGGER AS $$
BEGIN
-- При любом изменение менять дату updated_at
NEW.updated_at = CURRENT_TIMESTAMP;
-- Менять дату end_at при неактивном статусе
IF NEW.is_active = FALSE AND OLD.is_active = TRUE THEN
NEW.end_at = CURRENT_TIMESTAMP;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Реализована каскадная деактивация: при деактивации провайдера автоматически деактивируются все связанные с ним тарифы.
Базовые индексы для ускорения чтения:
CREATE INDEX CONCURRENTLY idx_город_провайдер_город_id ON город_провайдер (город_id);
CREATE INDEX CONCURRENTLY idx_ город_провайдер_провайдер_id ON город_провайдер (провайдер_id);
CREATE INDEX CONCURRENTLY idx_тарифы_город_провайдер_id ON тарифы (город_провайдеры_id); -- таблица с тарифами
Что дальше?
В следующей части я планирую рассказать о парсинге на Python vs Selenium.
Буду признателен за критику моей архитектуры БД и кода. Какие дополнения вы бы внесли в структуру SQL (ограничения, улучшение триггера, дополнительные индексы)?
Однажды наткнулся на статью о том, как кто-то реализовал игру DOOM полностью на SQL, стало интересно какие возможности этого языка. Насколько реально перевести весь бэкенд на SQL. Планирую применять Python для парсинга и записи данных в базу. Постепенно планирую заменять некоторые функции Python на SQL. Интересно, насколько можно заменить Python на SQL в реальных задачах пишите ваше мнение.
Комментарии (21)

shurutov
07.01.2026 05:33Абоненты годами сидят на устаревших и дорогих тарифах, не зная, что в их же доме уже давно доступны более выгодные предложения от конкурентов.
То ли плакать, то ли смеяться. Я знаю две возможности удешевить использование мобильной связи:
индивидуальное предложение, при этом всё зависит от оператора;
сменить оператора;
А буковки про более дешёвые тарифы - это не просто эффективно-менеджерский рекламно-маркетоложеский поток букв без подачи мысли, это прямое введение в заблуждение клиентов, что является нарушением закона.

shurutov
07.01.2026 05:33По технической части же - всё совсем грустно. Нет расширенного, хотя бы словесного, описания задачи, которая решается:
какие данные будут храниться для каждой из сущностей - тарифы, провайдеры, населённые пункты?
какую информацию будет возвращать ваше ПО?
в каком виде будет возвращать?
И т.д., и т.п.
Соответственно, нет полной структуры таблиц, что вызывает серьёзное удивление. Равно, как и кода, который будет создавать эти таблицы.
Зачем триггер? Вы отправляете запрос провайдеру, который возвращает информацию о том, что тариф переведён в архив, соответственно, ваше ПО должно просто обновить соответствующую информацию в БД. безо всяких триггеров.Резюме. Материал не тянет даже на экзаменационный вопрос из курса проектирования баз данных для подготовки инженеров профильных специальностей, не говоря уже о чём-то более объёмном, начиная с лабораторных работ.

MalblshProgrammist Автор
07.01.2026 05:33Учту замечания, дополню, отредактирую статью. Парсер будет получать тарифы у провайдеров и проверять, существуют ли эти тарифы в базе данных. Если обнаруживаются какие-либо отличия в тарифе (например, отличается скорость или название тарифа), то данный тариф помечается как архивный, а триггер автоматически заполняет поле
end_at, делая тариф неактивным.
rPman
Что? Я не помню ни дня, что бы архивный тариф опсоса (а я с ними со времен nokia 3310) был хуже по деньгам для клиента... единственное исключение, временные бонусы за переход между провайдерами или покупка симки.
Пару раз я повелся и менял тариф на предлагаемый опсосом
(больше, лучше, всего за +20р), и оба раза меня 'качественно обманывали',.. из последних помню теле2 за апгрейдил мой архив, добавив плюшек но удалив минимальный пакет sms (тогда все опсосы их предлагали, это замылило глаз и не увидел в описании этого), а так как отсылка sms (десятки в месяц и опсос это прекрасно понимал) мне все еще нужна была, в итоге мне приходилось до 100р докидывать к абонентке именно из-за них, а назад никак.
Но в любом случае, каким бы плохим не был когда то сейчас уже архивный тариф, он все еще в разы дешевле любого доступного тарифа (есть 100р 'интернет вещей', но пока мобильный интернет не доломан, на него переходить не спешу).
Единственные, кому архивные тарифы мешают - это сами операторы сотовой связи.
p.s. как вы описываете тариф и главное как их сравниваете?