From 5168819808ea8a7cdefc6fe29206e4196eb7602d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=A5=D0=B0?= =?UTF-8?q?=D0=BB=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Wed, 1 Jul 2026 20:49:42 +0000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20amountCalculating?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- amountCalculating | 478 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) create mode 100644 amountCalculating diff --git a/amountCalculating b/amountCalculating new file mode 100644 index 0000000..14a3861 --- /dev/null +++ b/amountCalculating @@ -0,0 +1,478 @@ +WITH +-- 1. Получаем все проверки ICMP за период +checks AS ( + SELECT + h.host AS host_name, + COALESCE(h.name, h.host) AS visible_name, + COALESCE(hinv."name", 'не указан') AS equipment_type, + COALESCE(hinv.notes, '') AS object_number, + CASE + WHEN hinv.location_lat IS NOT NULL AND hinv.location_lon IS NOT NULL + AND hinv.location_lat != '' AND hinv.location_lon != '' + THEN hinv.location_lat || ' ' || hinv.location_lon + WHEN hinv.location_lat IS NOT NULL AND hinv.location_lat != '' + THEN hinv.location_lat + WHEN hinv.location_lon IS NOT NULL AND hinv.location_lon != '' + THEN hinv.location_lon + ELSE '' + END AS coordinates, + TO_TIMESTAMP(hx.clock) AT TIME ZONE 'Europe/Moscow' AS check_time, + hx.value AS status + FROM hosts h + INNER JOIN items i + ON i.hostid = h.hostid + AND i.key_ = 'icmpping' + AND i.status = 0 + AND i.value_type = 3 + INNER JOIN history_uint hx + ON hx.itemid = i.itemid + LEFT JOIN host_inventory hinv + ON hinv.hostid = h.hostid + WHERE + h.status = 0 + AND h.host NOT LIKE '%WiMAX' + AND CAST(NULLIF(REGEXP_REPLACE(hinv.notes, 'ОБЪЕКТ №\s*', '', 'g'), '') AS INTEGER) + BETWEEN 1 AND 85 + AND TO_TIMESTAMP(hx.clock) >= TO_DATE(${start_dt}, 'yyyymmdd') - INTERVAL '1 day' + AND TO_TIMESTAMP(hx.clock) <= TO_DATE(${end_dt}, 'yyyymmdd') + INTERVAL '1 day' +), + +-- 2. Добавляем предыдущий статус +checks_with_prev AS ( + SELECT + *, + LAG(status) OVER (PARTITION BY host_name ORDER BY check_time) AS prev_status, + COALESCE(LEAD(check_time) OVER (PARTITION BY host_name ORDER BY check_time), NOW()) AS next_check_time + FROM checks +), + +-- 3. Определяем моменты смены статуса +status_changes AS ( + SELECT + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + check_time, + status, + prev_status, + CASE + WHEN DATE(next_check_time) > DATE(check_time) + THEN DATE_TRUNC('day', check_time) + INTERVAL '1 day' + ELSE next_check_time + END AS next_check_time, + CASE WHEN status != prev_status THEN 1 ELSE 0 END AS is_start + FROM checks_with_prev + UNION ALL + SELECT + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + DATE_TRUNC('day', next_check_time) + INTERVAL '00:00:00' AS check_time, + status, + prev_status, + next_check_time, + 1 AS is_start + FROM checks_with_prev + WHERE DATE(next_check_time) > DATE(check_time) +), + +-- 4. Нумеруем интервалы +intervals AS ( + SELECT + *, + SUM(is_start) OVER (PARTITION BY host_name ORDER BY check_time) AS interval_id + FROM status_changes + WHERE status IS NOT NULL + AND DATE_TRUNC('day', check_time) >= TO_DATE(${start_dt}, 'yyyymmdd') + AND DATE_TRUNC('day', check_time) <= TO_DATE(${end_dt}, 'yyyymmdd') +), + +-- 5. Группируем интервалы +grouped_intervals AS ( + SELECT + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + MIN(check_time) AS check_time, + MAX(next_check_time) AS next_check_time, + MAX(status) AS status + FROM intervals + GROUP BY + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + interval_id +), + +-- 6. Посуточная агрегация (часы доступности) +daily_stats_raw AS ( + SELECT + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + DATE(check_time) AS check_date, + ROUND( + SUM(CASE WHEN status = 1 THEN EXTRACT(EPOCH FROM (next_check_time - check_time)) / 3600 ELSE 0 END) * 2 + ) / 2 AS available_hours + FROM grouped_intervals + GROUP BY + host_name, + visible_name, + equipment_type, + object_number, + coordinates, + DATE(check_time) +), + +-- 7. Статистика по периферии (для маршрутизаторов) +peripheral_stats AS ( + SELECT + object_number, + check_date, + MAX(CASE WHEN available_hours > 12 THEN 1 ELSE 0 END) AS has_peripheral_work + FROM daily_stats_raw + WHERE equipment_type NOT LIKE 'Маршрутизатор%' + GROUP BY object_number, check_date +), + +-- 8. Собираем все дни месяца +all_dates AS ( + SELECT generate_series( + TO_DATE(${start_dt}, 'yyyymmdd'), + TO_DATE(${end_dt}, 'yyyymmdd'), + '1 day'::interval + )::date AS check_date +), + +-- 9. Кросс-джойн для каждого оборудования +equipment_dates AS ( + SELECT DISTINCT + visible_name, + equipment_type, + object_number, + coordinates, + ad.check_date + FROM daily_stats_raw d + CROSS JOIN all_dates ad +), + +-- 10. Присоединяем доступность ко всем дням +daily_stats_full AS ( + SELECT + ed.visible_name, + ed.equipment_type, + ed.object_number, + ed.coordinates, + ed.check_date, + COALESCE(dsr.available_hours, 0) AS available_hours + FROM equipment_dates ed + LEFT JOIN daily_stats_raw dsr + ON dsr.visible_name = ed.visible_name + AND dsr.check_date = ed.check_date +), + +-- 11. Финальная агрегация по оборудованию +equipment_summary AS ( + SELECT + visible_name, + equipment_type, + object_number, + coordinates, + STRING_AGG( + CASE + WHEN available_hours > 0 THEN TO_CHAR(check_date, 'DD.MM') || ':' || available_hours::TEXT + ELSE NULL + END, + '; ' ORDER BY check_date + ) AS daily_schedule, + COUNT(CASE WHEN available_hours > 12 THEN 1 END) AS days_gt_12h, + COUNT(CASE WHEN available_hours <= 12 AND available_hours > 0 THEN 1 END) AS days_le_12h, + ROUND(AVG(available_hours), 3) AS avg_hours, + -- Суммарные сутки доступности (факт) - по каждому устройству + ROUND(SUM(available_hours) / 24, 0) AS total_days_per_device + FROM daily_stats_full + GROUP BY + visible_name, + equipment_type, + object_number, + coordinates +), + +-- 12. Отдельно считаем работу маршрутизаторов +router_work AS ( + SELECT + d.object_number, + COUNT(DISTINCT d.check_date) AS router_work_days + FROM daily_stats_raw d + INNER JOIN peripheral_stats p + ON p.object_number = d.object_number + AND p.check_date = d.check_date + WHERE d.equipment_type LIKE 'Маршрутизатор%' + AND p.has_peripheral_work = 1 + GROUP BY d.object_number +), + +-- 13. Определяем этап +stages AS ( + SELECT DISTINCT + object_number, + CASE + WHEN object_number IN ('4','28','30','31','34','36','40','42','55','57','58','62','63','64','67','68','69','76','81','82','85') THEN '1 этап' + WHEN object_number IN ('1','9','12','27','35','38','41','43','44','48','49','51','53','54','60','65','77','84') THEN '2 этап' + ELSE '3 этап' + END AS stage + FROM equipment_summary +), + +-- 14. Подготавливаем данные для PIVOT-агрегации +daily_pivot AS ( + SELECT + visible_name, + equipment_type, + object_number, + coordinates, + check_date, + available_hours + FROM daily_stats_full +), + +-- 15. СВОДНАЯ ТАБЛИЦА по типам оборудования +summary_by_type AS ( + SELECT + equipment_type, + -- оборудование-сутки факт (сумма по всем устройствам типа) + ROUND(SUM(total_days_per_device), 0) AS fact_days, + -- Кол-во оборудования (статичное из Excel) + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 85 + WHEN equipment_type LIKE 'Детектор%' THEN 79 + WHEN equipment_type LIKE 'Метеостанция%' THEN 30 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 88 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 88 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 88 + ELSE 0 + END AS equipment_count, + -- оборудование-сутки план = 30 дней × количество оборудования + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 30 * 85 + WHEN equipment_type LIKE 'Детектор%' THEN 30 * 79 + WHEN equipment_type LIKE 'Метеостанция%' THEN 30 * 30 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 30 * 88 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 30 * 88 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 30 * 88 + ELSE 0 + END AS plan_days, + -- оборудование-сутки процент = факт / план + ROUND(SUM(total_days_per_device) / + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 30 * 85 + WHEN equipment_type LIKE 'Детектор%' THEN 30 * 79 + WHEN equipment_type LIKE 'Метеостанция%' THEN 30 * 30 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 30 * 88 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 30 * 88 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 30 * 88 + ELSE 1 + END, 9) AS percent, + -- Тариф в сутки (статичный из Excel) + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 43038.15 + WHEN equipment_type LIKE 'Детектор%' THEN 91293.59 + WHEN equipment_type LIKE 'Метеостанция%' THEN 53323.20 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 8868.36 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 4514.60 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 10883.72 + ELSE 0 + END AS daily_rate, + -- Итоговая сумма = Тариф × Кол-во дней в месяце × процент + ROUND( + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 43038.15 + WHEN equipment_type LIKE 'Детектор%' THEN 91293.59 + WHEN equipment_type LIKE 'Метеостанция%' THEN 53323.20 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 8868.36 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 4514.60 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 10883.72 + ELSE 0 + END * 30 * + (SUM(total_days_per_device) / + CASE + WHEN equipment_type LIKE 'Видеокамера%' THEN 30 * 85 + WHEN equipment_type LIKE 'Детектор%' THEN 30 * 79 + WHEN equipment_type LIKE 'Метеостанция%' THEN 30 * 30 + WHEN equipment_type LIKE 'Маршрутизатор%' THEN 30 * 88 + WHEN equipment_type LIKE 'Коммутационный шкаф%' THEN 30 * 88 + WHEN equipment_type LIKE 'Криптошлюз%' THEN 30 * 88 + ELSE 1 + END), + 2 + ) AS total_amount + FROM equipment_summary + GROUP BY equipment_type +) + +-- 16. Итоговый SELECT (детальный отчёт) +SELECT + 'ДЕТАЛЬНЫЙ ОТЧЁТ' AS section, + s.stage AS "Этап", + s.object_number AS "№ объекта", + e.equipment_type AS "Вид оборудования", + e.coordinates AS "Координаты", + MAX(CASE WHEN d.check_date = DATE '2026-06-01' THEN d.available_hours END) AS "01.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-02' THEN d.available_hours END) AS "02.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-03' THEN d.available_hours END) AS "03.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-04' THEN d.available_hours END) AS "04.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-05' THEN d.available_hours END) AS "05.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-06' THEN d.available_hours END) AS "06.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-07' THEN d.available_hours END) AS "07.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-08' THEN d.available_hours END) AS "08.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-09' THEN d.available_hours END) AS "09.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-10' THEN d.available_hours END) AS "10.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-11' THEN d.available_hours END) AS "11.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-12' THEN d.available_hours END) AS "12.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-13' THEN d.available_hours END) AS "13.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-14' THEN d.available_hours END) AS "14.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-15' THEN d.available_hours END) AS "15.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-16' THEN d.available_hours END) AS "16.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-17' THEN d.available_hours END) AS "17.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-18' THEN d.available_hours END) AS "18.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-19' THEN d.available_hours END) AS "19.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-20' THEN d.available_hours END) AS "20.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-21' THEN d.available_hours END) AS "21.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-22' THEN d.available_hours END) AS "22.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-23' THEN d.available_hours END) AS "23.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-24' THEN d.available_hours END) AS "24.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-25' THEN d.available_hours END) AS "25.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-26' THEN d.available_hours END) AS "26.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-27' THEN d.available_hours END) AS "27.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-28' THEN d.available_hours END) AS "28.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-29' THEN d.available_hours END) AS "29.06", + MAX(CASE WHEN d.check_date = DATE '2026-06-30' THEN d.available_hours END) AS "30.06", + e.days_gt_12h AS "Дней >12 ч", + e.days_le_12h AS "Дней ≤12 ч", + e.avg_hours AS "Среднее часов", + COALESCE(rw.router_work_days, 0) AS "Маршрутизатор работа в днях" +FROM equipment_summary e +LEFT JOIN stages s ON s.object_number = e.object_number +LEFT JOIN daily_pivot d + ON d.visible_name = e.visible_name + AND d.equipment_type = e.equipment_type + AND d.object_number = e.object_number + AND d.coordinates = e.coordinates +LEFT JOIN router_work rw ON rw.object_number = e.object_number +WHERE e.daily_schedule IS NOT NULL +GROUP BY + s.stage, + s.object_number, + e.equipment_type, + e.coordinates, + e.days_gt_12h, + e.days_le_12h, + e.avg_hours, + rw.router_work_days + +UNION ALL + +-- 17. СВОДНАЯ ТАБЛИЦА (как в Excel) +SELECT + 'СВОДНАЯ' AS section, + '' AS "Этап", + '' AS "№ объекта", + s.equipment_type AS "Вид оборудования", + '' AS "Координаты", + NULL AS "01.06", + NULL AS "02.06", + NULL AS "03.06", + NULL AS "04.06", + NULL AS "05.06", + NULL AS "06.06", + NULL AS "07.06", + NULL AS "08.06", + NULL AS "09.06", + NULL AS "10.06", + NULL AS "11.06", + NULL AS "12.06", + NULL AS "13.06", + NULL AS "14.06", + NULL AS "15.06", + NULL AS "16.06", + NULL AS "17.06", + NULL AS "18.06", + NULL AS "19.06", + NULL AS "20.06", + NULL AS "21.06", + NULL AS "22.06", + NULL AS "23.06", + NULL AS "24.06", + NULL AS "25.06", + NULL AS "26.06", + NULL AS "27.06", + NULL AS "28.06", + NULL AS "29.06", + NULL AS "30.06", + -- Колонки сводной таблицы в правильном порядке + s.fact_days AS "оборудование-сутки факт", + s.plan_days AS "оборудование-сутки план", + s.percent AS "оборудование-сутки процент", + s.equipment_count AS "Кол-во оборудования", + s.daily_rate AS "Тариф в сутки по договору ИТСР", + s.total_amount AS "Итоговая сумма к начислению" +FROM summary_by_type s + +UNION ALL + +-- 18. ИТОГОВАЯ СТРОКА +SELECT + 'ИТОГО' AS section, + '' AS "Этап", + '' AS "№ объекта", + 'Итоговая Сумма' AS "Вид оборудования", + '' AS "Координаты", + NULL AS "01.06", + NULL AS "02.06", + NULL AS "03.06", + NULL AS "04.06", + NULL AS "05.06", + NULL AS "06.06", + NULL AS "07.06", + NULL AS "08.06", + NULL AS "09.06", + NULL AS "10.06", + NULL AS "11.06", + NULL AS "12.06", + NULL AS "13.06", + NULL AS "14.06", + NULL AS "15.06", + NULL AS "16.06", + NULL AS "17.06", + NULL AS "18.06", + NULL AS "19.06", + NULL AS "20.06", + NULL AS "21.06", + NULL AS "22.06", + NULL AS "23.06", + NULL AS "24.06", + NULL AS "25.06", + NULL AS "26.06", + NULL AS "27.06", + NULL AS "28.06", + NULL AS "29.06", + NULL AS "30.06", + SUM(s.fact_days) AS "оборудование-сутки факт", + SUM(s.plan_days) AS "оборудование-сутки план", + ROUND(SUM(s.fact_days) / SUM(s.plan_days), 9) AS "оборудование-сутки процент", + SUM(s.equipment_count) AS "Кол-во оборудования", + NULL AS "Тариф в сутки по договору ИТСР", + SUM(s.total_amount) AS "Итоговая сумма к начислению" +FROM summary_by_type s + +ORDER BY section DESC, "Этап", "№ объекта", "Вид оборудования"; \ No newline at end of file