261 lines
9.5 KiB
SQL
261 lines
9.5 KiB
SQL
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 check_time,
|
||
-- Статус: 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 h.host not like '%WiMAX'
|
||
and h.host not like '%DP%'
|
||
and h.host not like '%CO%'
|
||
and ((CAST(nullif(regexp_replace(hinv.notes, 'ОБЪЕКТ №\s*', '', 'g'),'') as INTEGER)
|
||
in (1,2,4,5,8,9,11,12,14,23,24,25,27,28,29,30,31,32,34,35,36,38,39,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,75,76,77,81,82,84,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' )
|
||
or (CAST(nullif(regexp_replace(hinv.notes, 'ОБЪЕКТ №\s*', '', 'g'),'') as INTEGER)
|
||
in (6,10,16,74,78)
|
||
AND TO_TIMESTAMP(hx.clock) >= to_date('20260709', 'yyyymmdd') - interval '1 day'
|
||
AND TO_TIMESTAMP(hx.clock) <= to_date(${end_dt}, 'yyyymmdd') + interval '1 day' ))
|
||
--and hinv.notes is null
|
||
|
||
),
|
||
|
||
-- 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), date_trunc('day', check_time) + interval '1 day') 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', next_check_time) + interval '00:00:00'
|
||
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 ((regexp_replace(object_number, 'ОБЪЕКТ №\s*', '', 'g')::INT in (6,10,16,74,78)
|
||
AND date_trunc('day', check_time) >= to_date('20260709', 'yyyymmdd')
|
||
AND date_trunc('day', check_time) <= to_date(${end_dt}, 'yyyymmdd'))
|
||
or (regexp_replace(object_number, 'ОБЪЕКТ №\s*', '', 'g')::INT not in (6,10,16,74,78) and date_trunc('day', check_time) >= to_date(${start_dt}, 'yyyymmdd') and date_trunc('day', check_time) <= to_date(${end_dt}, 'yyyymmdd')))
|
||
and check_time <= now()
|
||
),
|
||
|
||
-- 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
|
||
),
|
||
|
||
-- Определение маршрутизатора (заглушка)
|
||
router as
|
||
(
|
||
select
|
||
m.host_name,
|
||
m.visible_name,
|
||
m.equipment_type,
|
||
v.object_number,
|
||
v.coordinates,
|
||
v.check_time,
|
||
v.next_check_time,
|
||
v.status
|
||
from grouped_intervals v
|
||
join
|
||
(
|
||
select host_name, visible_name, object_number, equipment_type, date_trunc('day', check_time) as time_start_dt
|
||
from grouped_intervals
|
||
where equipment_type like 'Маршрутизатор%'
|
||
group by 1,2,3,4,5
|
||
) m on m.object_number = v.object_number
|
||
where v.equipment_type like 'Видеокамера%'
|
||
and m.time_start_dt = date_trunc('day', v.check_time)
|
||
),
|
||
|
||
daily_stats AS (
|
||
select *,
|
||
extract('hour' from available_hours)::int as a_h,
|
||
extract('minute' from available_hours)::int as a_m,
|
||
extract('second' from available_hours)::int as a_s,
|
||
extract('hour' from unavailable_hours)::int as u_h,
|
||
extract('minute' from unavailable_hours)::int as u_m,
|
||
extract('second' from unavailable_hours)::int as u_s
|
||
from
|
||
(
|
||
select
|
||
host_name,
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates,
|
||
check_date,
|
||
case
|
||
when available_hours = '1 day' then '24:00:00'::interval
|
||
else available_hours
|
||
end as available_hours,
|
||
case
|
||
when unavailable_hours = '1 day' then '24:00:00'::interval
|
||
when available_hours + unavailable_hours != '24:00:00'::interval then '24:00:00'::interval - available_hours
|
||
else unavailable_hours
|
||
end as unavailable_hours
|
||
from
|
||
(
|
||
SELECT
|
||
host_name,
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates,
|
||
DATE(check_time) AS check_date,
|
||
sum(case when status = 1 then next_check_time - check_time else '00:00:00'::interval end) AS available_hours,
|
||
sum(case when status = 0 then next_check_time - check_time else '00:00:00'::interval end) AS unavailable_hours
|
||
--sum(next_check_time - check_time) AS all_hours
|
||
FROM
|
||
(
|
||
select * from grouped_intervals where equipment_type not like 'Маршрутизатор%'
|
||
union all
|
||
select * from router
|
||
) g
|
||
GROUP BY
|
||
host_name,
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates,
|
||
DATE(check_time)
|
||
) s
|
||
) f
|
||
),
|
||
|
||
daily_graph AS (
|
||
SELECT
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates,
|
||
max(case when object_number similar to '%№%' then regexp_replace(object_number, 'ОБЪЕКТ №\s*', '', 'g')::int else null end) as object_num,
|
||
STRING_AGG(
|
||
TO_CHAR(check_date, 'DD.MM')
|
||
|| ': Доступно ' || a_h || 'ч.' || a_m || 'м.' || a_s|| 'с.'
|
||
|| ', Недоступно ' || u_h || 'ч.' || u_m || 'м.' || u_s ||'с.',
|
||
'; ' ORDER BY check_date
|
||
) AS daily_schedule
|
||
FROM daily_stats
|
||
GROUP BY
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates
|
||
),
|
||
|
||
device_summary AS (
|
||
SELECT
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates,
|
||
SUM(CASE WHEN (a_h + a_m/60 + a_s/3600) > 12 THEN 1 ELSE 0 END) AS days_available_gt_12h,
|
||
SUM(CASE WHEN (a_h + a_m/60 + a_s/3600) <= 12 THEN 1 ELSE 0 END) AS days_unavailable_ge_12h
|
||
FROM daily_stats
|
||
GROUP BY
|
||
visible_name,
|
||
equipment_type,
|
||
object_number,
|
||
coordinates
|
||
)
|
||
|
||
SELECT
|
||
ROW_NUMBER() OVER (ORDER BY dg.object_num nulls last, dg.equipment_type) AS "№ п/п",
|
||
dg.equipment_type AS "Тип оборудования",
|
||
case when dg.object_num is null then 'на инвентаризации' else dg.object_number end AS "Номер_объекта_ИТС_РО",
|
||
dg.coordinates AS "Координаты_расположения",
|
||
dg.daily_schedule AS "Посуточный график функц оборуд",
|
||
ds.days_available_gt_12h AS "Количество суток доступности",
|
||
ds.days_unavailable_ge_12h AS "Количество суток недоступности"
|
||
FROM daily_graph dg
|
||
INNER JOIN device_summary ds
|
||
ON ds.visible_name = dg.visible_name
|
||
ORDER BY 1;
|