惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
量子位
L
LINUX DO - 热门话题
Jina AI
Jina AI
J
Java Code Geeks
U
Unit 42
V
Vulnerabilities – Threatpost
The Hacker News
The Hacker News
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
WordPress大学
WordPress大学
D
Docker
T
The Exploit Database - CXSecurity.com
博客园 - Franky
Project Zero
Project Zero
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
MongoDB | Blog
MongoDB | Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
Simon Willison's Weblog
Simon Willison's Weblog
月光博客
月光博客
V
Visual Studio Blog
腾讯CDC
The Cloudflare Blog
V
V2EX
C
Cybersecurity and Infrastructure Security Agency CISA
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Security Latest
Security Latest
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary
I
Intezer
S
Securelist
A
Arctic Wolf
小众软件
小众软件
P
Privacy International News Feed
Spread Privacy
Spread Privacy
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyberwarzone
Cyberwarzone
T
Tailwind CSS Blog
Latest news
Latest news
H
Help Net Security
S
Schneier on Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Proofpoint News Feed
Scott Helme
Scott Helme
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Последовательное иерархическое распределение сумм. Создание БД. Распределение сумм по правилам средствами PostgreSQL
Орлянский Владимир · 2026-05-28 · via Все публикации подряд на Хабре

Описание

В первой статье была описана задача распределения сумм затрат между отделами по заданным правилам, с учётом того что отделы могут передавать свои затраты на другие отделы. Задача этой и следующей статей — показать, как эту задачу можно реализовать относительно просто и легко, не прибегая к созданию больших приложений.

В данной статье будет описана структура PostgreSQL-базы данных и реализация данной задачи с помощью SQL в варианте PL/pgSQL. Реализация на Apache Spark будет описана в следующей статье.

В качестве примера будут созданы настройки распределения и входные данные для:

  1. Примера из первой статьи, распределения одной входной суммы затрат 1000 на 3 отдела.

  2. Распределение нескольких тысяч входных сумм затрат на произвольное количество отделов. Скрипт будет настраиваемый.

Структура базы данных

Общее описание

Все примеры будут делаться относительно данных описанных в первой статье.

Скрипт создания базы данных для данной задачи находится в репозитории.

Правила распределения

В базе данных предусмотрены наборы правил распределения сумм затрат. Список наборов правил распределения хранится в таблице rules_headers. В ней хранится список правил и сумма, начиная с которой пришедшие значения могут быть переданы на другие отделы.

Команду создания таблицы rules_headers можно найти в репозитории.

Содержимое таблицы rules_headers для примера описанного в первой статье показано в таблице:

department_name

id

limit

Отдел 1

1

100

Отдел 2

2

100

Отдел 3

3

[NULL]

Пришедшие суммы должны распределяться по определенным формулам. Для хранения формул и связи их с набором правил создана таблица rules_rows. В ней хранится связь с набором правил (заголовком), признак отключения дальнейшего распределения пришедших сумм и ссылка на отдел, на который распределяется пришедшая сумма. В данной реализации отдел равен группе затрат.

Содержимое таблицы rules_rows для примера описанного в первой статье показано в таблице:

id

formula

disable_child_creation

rule_header_id

dest_rule_id

1

{X}*0.25

true

1

1

2

{X}*0.25

[NULL]

1

2

3

{X}*0.50

[NULL]

1

3

4

{X}*0.30

true

2

2

5

{X}*0.60

[NULL]

2

1

6

{X}*0.10

[NULL]

2

3

7

{X}

true

3

3

Поле rule_header_id связывает правило распределения суммы с набором правил; в этом примере это синоним отдела.

Пришедшие суммы должны распределяться по формулам. Формулы описаны в простом формате где {X} заменяется на сумму для распределения на заголовок/отдел.

Поле disable_child_creation показывает, нужно или нет запускать дальнейшие распределения, если пришедшая сумма больше порога указанного в записи заголовка/отдела.

Поле dest_rule_id указывает, на какой отдел переносить рассчитанную сумму затрат.

Смысл формул можно посмотреть в первой статье в разделе «Графическое описание».

Таблица входных сумм для распределения

Расчет распределения сумм затрат нужно начинать с набора этих сумм. Сразу же нужно предусмотреть способ проверки результатов.

Для хранения набора пришедших для распределения затрат служит таблица costs_input.

Содержимое таблицы costs_input для примера описанного в первой статье показано в таблице:

id

department_name

calc_name

cost_sum

1

Отдел 1

Пример из статьи

1,000

Поле department_name содержит имя отдела для распределения затрат. Это поле аналогично полю department_name таблицы rules_headers.

Поле cost_sum содержит сумму, которую нужно распределить.

Результаты распределения

Результаты распределения затрат сохраняются в таблице costs_output.

Пример результатов распределения затрат для входных данных, описанных в первой статье, показан в таблице:

id

cost_input_id

rules_rows_id

parent_row_id

summ

1

1

1

[NULL]

250

2

1

2

[NULL]

250

3

1

3

[NULL]

500

4

1

4

2

75

5

1

5

2

150

6

1

6

2

25

7

1

1

5

37.5

8

1

2

5

37.5

9

1

3

5

75

Поле cost_input_id содержит ссылку на строку входных сумм распределения. Наличие этой строки позволит в дальнейшем легко проверить результат распределения.

Поле rules_rows_id содержит ссылку на строку распределения из таблицы rules_rows.

Затраты распределяются иерархически до момента прихода суммы в строку затрат где стоит запрет на дальнейшее распределение затрат или при получении суммы затрат меньше, чем лимит, после которого затраты распределяются. Соответственно, нужно хранить связь между начальными и подчиненными затратами. Для этой цели служит поле parent_row_id, в котором содержится ссылка на предыдущий этап расчета затрат, если текущий уровень расчета второй и ниже. При расчете первого уровня (сумм из таблицы costs_input) в этом поле находится NULL.

Поле summ хранит сумму распределенных затрат.

Эта таблица содержит суммы, аналогичные суммам, описанным в примере распределения затрат из первой статьи.

Реализация распределения затрат с помощью SQL

При создании базы данных с примером распределения было обнаружено, что описанную задачу можно легко решить с помощью стандартных средств PostgreSQL.

Пример решения с помощью SQL приведён в скрипте репозитория.

Так как данное SQL-решение будет сравниваться с решением на Apache Spark, то при его реализации были задействованы преимущества SQL.

Краткое описание SQL решения

SQL-решение в скрипте работает следующим образом:

  1. Из таблицы costs_input берутся входные данные для распределения и сразу же объединяются с правилами, что позволяет сразу же провести распределение первого уровня.

  2. Для расчета суммы затрат берется формула распределения затрат из таблицы rules_rows, в ней заменяются символы {X} на пришедшую сумму и полученная формула вычисляется через SQL-команду.

  3. Запускается цикл который выбирает строки самого нижнего уровня (у которых нет подчиненных строк через поле parent_row_id), сумма затрат по которым больше порога начала распределения затрат, и строка правил по которым не имеет признака остановки расчетов. Пока такие строки находятся их значения распределяются согласно формуле из таблицы rules_rows.

  4. Так как в процессе применения формул распределения затрат неизбежно возникают ошибки округления они исправляются с помощью SQL-команды UPDATE, объединенной с логикой расчета ошибок округления через CTE. Это позволяет выполнить исправления ошибок округления по всем рассчитанным значениям одной командой. Логика поиска ошибок округления будет описана в разделе проверки расчетов.

Почему 4-й пункт нельзя считать «подгонкой результатов» под нужные значения. При исправлении ошибок округления ищется разница между входящей суммой затрат и уже распределенной суммой затрат по всем отделам. То есть сравнивается результат распределения с суммой исходных затрат. На момент исправления ошибок округления затраты уже распределены на произвольное количество отделов. И исправляется именно последний распределенный уровень затрат.

Проверка результатов

Полученные расчеты нужно проверить. Для проверки расчетов служит скрипт проверки tester.sql. У него следующая краткая логика работы:

  1. Собираются полученные суммы результатов распределения для каждой строки входных сумм затрат.

  2. Для каждой суммы входных затрат выбирается первое распределение.

  3. Полученный результат объединяется с таблицей входных сумм.

  4. Фильтруются строки в которых сумма пришедших затрат не равна сумме распределенных затрат привязанных к пришедшим затратам.

Описание скриптов загрузки данных и запуска расчетов

В SQL-решении представлены два скрипта загрузки исходных данных для расчета.

  1. Скрипт, который загружает данные, аналогичные описанному примеру в первой статье.

  2. Скрипт, позволяющий создать в базе несколько тысяч строк для распределения затрат и несколько тысяч правил их распределения с нужным количеством долей распределения на другие отделы.

Описание скрипта создания большого объема данных для расчета

Скрипт создания большого объема данных для расчета расположен в репозитории.

Переменная departments_count позволяет указать количество отделов и строк входных данных для распределения.

Порядок тестирования SQL-решения

Для запуска SQL решения необходимо выполнить следующие действия:

  1. Создать пустую базу данных и выполнить в ней скрипт создания структуры базы данных.

  2. Запустить скрипт создания примера из первой статьи или скрипт создания большого объема входных данных для расчета. Можно его назвать скрипт нагрузочного тестирования. При желании в скрипте нагрузочного тестирования можно увеличить или уменьшить переменную departments_count что позволит увеличить или уменьшить нагрузку на PostgreSQL-сервер.

  3. Запустить скрипт распределения затрат. По сути этот скрипт и является SQL-решением для распределения затрат.

  4. Запустить скрипт проверки результатов.

Выводы после создания SQL решения

Созданное SQL-решение показало, что решить задачу распределения затрат по правилам можно с помощью простых средств доступных в обычном PostgreSQL. На компьютере Core i5 10-й серии расчет затрат для 5000 входящих строк и распределения их на 5—10 других отделов что вызывает каскадное распределение затрат занимает меньше 10 секунд.

SQL-решение не потребовало использование рекурсивных SQL-запросов.

Исправление ошибок округления удалось выполнить с помощью одного SQL-запроса.

Однако есть и выявленные сложности. SQL-решение начинает работать значительно медленнее после 10 тысяч строк. При расчете распределения всегда используется только один процессор.

В целом можно сказать что PostgreSQL может использоваться при построении приложений распределения затрат при условии, что количество входящих данных не будет превышать десятки тысяч строк и правила не будут разветвляться более чем на 10 направлений.