-- ============================================================================= -- Отчет повременного учета работоспособности периферийного оборудования -- Период: май 2026 -- Исключены устройства, начинающиеся на IRZ -- Тип оборудования берется из инвентаризации Zabbix (поле type) -- Координаты: широта + долгота из полей location_lat и location_lon -- ============================================================================= WITH -- 1. Получаем все проверки ICMP за период checks AS ( SELECT h.host AS host_name, COALESCE(h.name, h.host) AS visible_name, -- Тип оборудования из инвентаризации (поле type), если пусто - берем hardware COALESCE(hinv."name", 'не указан') AS equipment_type, -- Номер объекта ИТС РО (храним в поле notes инвентаризации) 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 ts, -- Статус: 1 = доступно, 0 = недоступно 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 -- unsigned numeric 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 COALESCE(h.name, h.host) not like '%WIMAX' -- Исключаем оборудование, начинающееся на IRZ (любой регистр) -- AND UPPER(h.host) NOT LIKE 'IRZ%' -- Период: май 2026 года (измените под свои даты) and hinv.notes is not null and hinv.notes <> '' --and hinv.notes = 'ОБЪЕКТ № 67' --and hinv.name like '%Маршрутизатор%' and CAST(nullif(regexp_replace(hinv.notes, 'ОБЪЕКТ №\s*', '', 'g'),'') as INTEGER) --between 1 and 85 in (1,2,4,5,8,9,11,12,14,24,25,27,28,29,30,31,32,34,35,36,38,40,41,42,43,44,45,46,48,49,50,51,53,54,55,57,58,60,62,63,64,65,66,67,68,69,70,76,77,81,82,84,85) --AND hx.clock >= EXTRACT(EPOCH FROM TIMESTAMP '2026-06-01 00:00:00') --AND hx.clock < EXTRACT(EPOCH FROM TIMESTAMP '2026-06-05 00:00:00') 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 ts) AS prev_status, coalesce(lead(ts) OVER (PARTITION BY host_name ORDER BY ts), date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + interval '1 month') AS next_ts FROM checks ), --3. Определяем моменты смены статуса (начало нового интервала) status_changes AS ( SELECT host_name, visible_name, equipment_type, object_number, coordinates, ts, status, prev_status, case when DATE(next_ts) > DATE(ts) then date_trunc('day', next_ts) + interval '00:00:00' else next_ts - interval '1 sec' end as next_ts, case when DATE(next_ts) > DATE(ts) then date_trunc('day', next_ts) + interval '00:00:00' else next_ts 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_ts) + interval '00:00:00' as ts, status, prev_status, next_ts - interval '1 sec' as next_ts, next_ts as next_check_time, 1 as is_start FROM checks_with_prev where DATE(next_ts) > DATE(ts) ), -- 4. Нумеруем интервалы для каждого устройства intervals AS ( SELECT *, SUM(is_start) OVER (PARTITION BY host_name ORDER BY ts) AS interval_id FROM status_changes WHERE status IS NOT null and date_trunc('day', ts) >= to_date(${start_dt}, 'yyyymmdd') and date_trunc('day', ts) <= to_date(${end_dt}, 'yyyymmdd') ), -- 5. Агрегируем каждый непрерывный интервал grouped_intervals AS ( SELECT host_name, visible_name, equipment_type, object_number, coordinates, max(regexp_replace(object_number, 'ОБЪЕКТ №\s*', '', 'g'))::int as object_num, MIN(ts) AS time_start, MAX(next_ts) AS time_end, MAX(next_check_time) AS time_end_all, MAX(status) AS status FROM intervals GROUP BY host_name, visible_name, equipment_type, object_number, coordinates, interval_id ), -- 5_1. Определение маршрутизатора (заглушка) router as ( select m.host_name, m.visible_name, m.equipment_type, v.object_number, v.coordinates, v.object_num, v.time_start, v.time_end, v.time_end_all, v.status from grouped_intervals v join ( select host_name, visible_name, object_number, equipment_type, date_trunc('day', time_start) as time_start_dt, object_num from grouped_intervals where equipment_type like 'Маршрутизатор%' group by 1,2,3,4,5,6 ) m on m.object_num = v.object_num where v.equipment_type like 'Видеокамера%' and time_start_dt = date_trunc('day', v.time_start) ), union_zab as ( select * , case when equipment_type like 'Метеостанция%' then '1' when equipment_type like 'Видеокамера%' then '2' when equipment_type like 'Маршрутизатор%' then '3' when object_num = 38 and equipment_type like 'Детектор транспортных потоков CTfind DTM 1' then 'Детектор транспортных потоков №1' when object_num = 38 and equipment_type like 'Детектор транспортных потоков CTfind DTM 2' then 'Детектор транспортных потоков №2' else equipment_type end as equipment_id , DATE(time_start) AS date_check , row_number() over(partition by DATE(time_start), object_num, equipment_type order by time_start desc) as rn from ( select * from grouped_intervals where equipment_type not like 'Маршрутизатор%' union all select * from router ) u ), sum_interval as ( select equipment_id, object_num, date_check, case when available_hours = '1 day' then '24:00:00'::interval else available_hours::interval end as available_hours from ( SELECT equipment_id, object_num, date_check, sum(time_end_all - time_start) AS available_hours FROM union_zab u where status = 1 GROUP BY equipment_id, object_num, date_check ) s ), gant as ( select DATE(date_check) as date_check, object_name, equipment_type, coordinates , regexp_replace(object_name, 'ОБЪЕКТ №\s*', '', 'g')::int as object_num , case when equipment_type like 'Метеостанция%' then '1' when equipment_type like 'Видеокамера%' then '2' when equipment_type like 'Роутер%' then '3' else equipment_type end as equipment_id , time_start as sum_hours , coalesce(LPAD(FLOOR(time_start)::INT::TEXT, 2, '0') || ':' || LPAD(FLOOR((time_start- FLOOR(time_start)::INT) * 60)::INT::TEXT, 2, '0') || ':' || LPAD(ROUND(((time_start - FLOOR(time_start)::INT) * 60 - FLOOR((time_start- FLOOR(time_start)::INT) * 60)::INT) * 60)::INT::TEXT, 2, '0'), '00:00:00')::interval as aval from ( select object_name, equipment_type, d01 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) as date_check from gantt_contractor union all select object_name, equipment_type, d02 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '1 day'from gantt_contractor union all select object_name, equipment_type, d03 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '2 day'from gantt_contractor union all select object_name, equipment_type, d04 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '3 day'from gantt_contractor union all select object_name, equipment_type, d05 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '4 day'from gantt_contractor union all select object_name, equipment_type, d06 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '5 day'from gantt_contractor union all select object_name, equipment_type, d07 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '6 day'from gantt_contractor union all select object_name, equipment_type, d08 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '7 day'from gantt_contractor union all select object_name, equipment_type, d09 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '8 day'from gantt_contractor union all select object_name, equipment_type, d10 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '9 day'from gantt_contractor union all select object_name, equipment_type, d11 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '10 day'from gantt_contractor union all select object_name, equipment_type, d12 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '11 day'from gantt_contractor union all select object_name, equipment_type, d13 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '12 day'from gantt_contractor union all select object_name, equipment_type, d14 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '13 day'from gantt_contractor union all select object_name, equipment_type, d15 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '14 day'from gantt_contractor union all select object_name, equipment_type, d16 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '15 day'from gantt_contractor union all select object_name, equipment_type, d17 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '16 day'from gantt_contractor union all select object_name, equipment_type, d18 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '17 day'from gantt_contractor union all select object_name, equipment_type, d19 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '18 day'from gantt_contractor union all select object_name, equipment_type, d20 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '19 day'from gantt_contractor union all select object_name, equipment_type, d21 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '20 day'from gantt_contractor union all select object_name, equipment_type, d22 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '21 day'from gantt_contractor union all select object_name, equipment_type, d23 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '22 day'from gantt_contractor union all select object_name, equipment_type, d24 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '23 day'from gantt_contractor union all select object_name, equipment_type, d25 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '24 day'from gantt_contractor union all select object_name, equipment_type, d26 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '25 day'from gantt_contractor union all select object_name, equipment_type, d27 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '26 day'from gantt_contractor union all select object_name, equipment_type, d28 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '27 day'from gantt_contractor union all select object_name, equipment_type, d29 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '28 day'from gantt_contractor union all select object_name, equipment_type, d30 as time_start, coordinates, date_trunc('month', to_date(${start_dt}, 'yyyymmdd')) + INTERVAL '29 day'from gantt_contractor ) g where date_check >= to_date(${start_dt}, 'yyyymmdd') and date_check <= to_date(${end_dt}, 'yyyymmdd') ), gant_interval as ( select date_check, object_name, equipment_type, object_num, equipment_id, coordinates , 1 as status, INTERVAL '00:00:00' as time_start_tm, INTERVAL '24:00:00' as time_end_tm from gant where sum_hours = 24 union all select date_check, object_name, equipment_type, object_num, equipment_id, coordinates , 1 as status, INTERVAL '00:00:00' as time_start_tm, aval::interval as time_end_tm from gant where sum_hours > 0 and sum_hours < 24 union all select date_check, object_name, equipment_type, object_num, equipment_id, coordinates , 0 as status, aval::interval + INTERVAL '1 second' as time_start_tm, INTERVAL '00:00:00' as time_end_tm from gant where sum_hours > 0 and sum_hours < 24 union all select date_check, object_name, equipment_type, object_num, equipment_id, coordinates , 0 as status, INTERVAL '00:00:00' as time_start_tm, INTERVAL '24:00:00' as time_end_tm from gant where sum_hours = 0 union all select date_check, object_name, equipment_type, object_num, equipment_id, coordinates , 0 as status, aval::interval, aval::interval from gant where sum_hours is null ), gener as ( select g.*, i.available_hours, g.time_end_tm - i.available_hours as diff_tm , case when g.time_end_tm > i.available_hours then 1 else 0 end as diff_fl from gant_interval g left join sum_interval i on g.object_num = i.object_num and g.equipment_id = i.equipment_id and g.date_check = i.date_check where abs(extract('minute' from g.time_end_tm - i.available_hours)::int) > 5 and g.status = 1 union all select g.*, i.available_hours, INTERVAL '00:00:00' as diff_tm, null as diff_fl from gant_interval g left join sum_interval i on g.object_num = i.object_num and g.equipment_id = i.equipment_id and g.date_check = i.date_check where i.available_hours is null ), gener_gant_interval as ( select gi.date_check, g.equipment_id, g.object_num, z.status , case when z.status = 1 and z.rn = 1 then z.time_start - DATE_trunc('day', z.time_start) - diff_tm when z.status = 0 and z.rn = 2 then z.time_start - DATE_trunc('day', z.time_start) when z.status = 0 and z.rn = 1 then z.time_start - DATE_trunc('day', z.time_start) - diff_tm when z.status = 1 and z.rn = 2 then z.time_start - DATE_trunc('day', z.time_start) else z.time_start - DATE_trunc('day', z.time_start) end as time_start_tm , case when z.status = 1 and z.rn = 1 then z.time_end - DATE_trunc('day', z.time_end) when z.status = 0 and z.rn = 2 then z.time_end - DATE_trunc('day', z.time_end) - diff_tm when z.status = 0 and z.rn = 1 then z.time_end - DATE_trunc('day', z.time_end) when z.status = 1 and z.rn = 2 then z.time_end - DATE_trunc('day', z.time_end) - diff_tm else z.time_end - DATE_trunc('day', z.time_end) end as time_end_tm from gener g join gant_interval gi on g.object_num = gi.object_num and g.equipment_id = gi.equipment_id and g.date_check = gi.date_check join union_zab z on g.object_num = z.object_num and g.equipment_id = z.equipment_id and g.date_check = z.date_check where g.diff_fl = 1 --в zabbix < union select gi.date_check, g.equipment_id, g.object_num, z.status , case when z.status = 1 and z.rn = 1 then z.time_start - DATE_trunc('day', z.time_start) + diff_tm when z.status = 0 and z.rn = 2 then z.time_start - DATE_trunc('day', z.time_start) when z.status = 0 and z.rn = 1 then z.time_start - DATE_trunc('day', z.time_start) + diff_tm when z.status = 1 and z.rn = 2 then z.time_start - DATE_trunc('day', z.time_start) else z.time_start - DATE_trunc('day', z.time_start) end as time_start_tm , case when z.status = 1 and z.rn = 1 then z.time_end - DATE_trunc('day', z.time_end) when z.status = 0 and z.rn = 2 then z.time_end - DATE_trunc('day', z.time_end) + diff_tm when z.status = 0 and z.rn = 1 then z.time_end - DATE_trunc('day', z.time_end) when z.status = 1 and z.rn = 2 then z.time_end - DATE_trunc('day', z.time_end) + diff_tm else z.time_end - DATE_trunc('day', z.time_end) end as time_end_tm from gener g join gant_interval gi on g.object_num = gi.object_num and g.equipment_id = gi.equipment_id and g.date_check = gi.date_check join union_zab z on g.object_num = z.object_num and g.equipment_id = z.equipment_id and g.date_check = z.date_check where g.diff_fl = 0 --в zabbix > ), main as ( select g.date_check as date_check , coalesce(u.object_num, g.object_num) as object_num , coalesce(u.equipment_id, g.equipment_id) as equipment_id , coalesce(u.equipment_type, g.equipment_type) as equipment_type , coalesce(u.object_number, g.object_name) as object_name , greatest(u.coordinates::varchar, g.coordinates::varchar) as coordinates from gant g left join union_zab u on u.object_num = g.object_num and g.equipment_id = u.equipment_id group by 1,2,3,4,5,6 ) -- 6. Финальный вывод отчета SELECT ROW_NUMBER() OVER (order by m.date_check, m.object_num nulls last, m.equipment_type, u.time_start) AS "№ п/п", TO_CHAR(DATE(m.date_check), 'DD.MM.YYYY') AS "Дата", m.equipment_type as "Тип оборудования", m.object_name as "Номер_объекта_ИТС_РО", m.coordinates as "Координаты_расположения", TO_CHAR(u.time_start, 'HH24:MI:SS') AS "Время начала статуса", TO_CHAR(u.time_end, 'HH24:MI:SS') as "Время окончания статуса", CASE WHEN u.status = 1 THEN 'доступно' ELSE 'недоступно' END AS "Статус" FROM main m join ( select * from ( select gi.date_check, gi.equipment_id, gi.object_num, gi.status, gi.time_start_tm as time_start, gi.time_end_tm as time_end from gener g join gant_interval gi on g.object_num = gi.object_num and g.equipment_id = gi.equipment_id and g.date_check = gi.date_check where g.diff_fl is null union all select date_check, equipment_id, object_num, status, time_start_tm, time_end_tm from gener_gant_interval union all select uz.date_check, uz.equipment_id, uz.object_num, uz.status, uz.time_start - DATE_trunc('day', uz.time_start), uz.time_end - DATE_trunc('day', uz.time_start) from union_zab uz left join gener_gant_interval ggi on uz.object_num = ggi.object_num and uz.equipment_id = ggi.equipment_id and uz.date_check = ggi.date_check where ggi.date_check is null ) r ) u on m.object_num = u.object_num and m.equipment_id = u.equipment_id and m.date_check = u.date_check ORDER BY 1 ;