Files
zabbixSql/hardwareAvailabilityReport.sql
T

206 lines
7.8 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
-- Исключаем оборудование, начинающееся на IRZ (любой регистр)
--AND UPPER(h.host) NOT LIKE 'IRZ%'
-- Период: май 2026 года (измените под свои даты)
--and hinv.notes = 'ОБЪЕКТ № 5'
--and hinv."name" = 'Метеостанция Кондор'
and CAST(nullif(regexp_replace(hinv.notes, 'ОБЪЕКТ №\s*', '', 'g'),'') as INTEGER)
between 1 and 85 --in (44,77,43,54,27,48,12,49,65,9,60,35,38,1,51,41,53,84,8)
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'
--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 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
),
daily_stats AS (
select *,
case when unavailable_hours is not null then coalesce(extract('hour' from available_hours)::int,0) else 24 end AS a_h,
coalesce(extract('minute' from available_hours)::int,0) AS a_m,
coalesce(extract('second' from available_hours)::int,0) AS a_s,
case when available_hours is not null then coalesce(extract('hour' from unavailable_hours)::int,0) else 24 end AS u_h,
coalesce(extract('minute' from unavailable_hours)::int,0) AS u_m,
coalesce(extract('second' from unavailable_hours)::int,0) AS u_s
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 null end) AS available_hours,
sum(case when status = 0 then next_check_time - check_time else null end) AS unavailable_hours
--sum(next_check_time - check_time) AS all_hours
FROM grouped_intervals
GROUP BY
host_name,
visible_name,
equipment_type,
object_number,
coordinates,
DATE(check_time)
) 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/360) > 12 THEN 1 ELSE 0 END) AS days_available_gt_12h,
SUM(CASE WHEN (a_h + a_m/60 + a_s/360) <= 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;