Files
zabbixSql/hardwareAvailabilityReport.sql
T

261 lines
9.5 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;