Вопрос или проблема
У меня есть таблица с location_id
, status_date
и sys_run_hrs
. Я хотел бы создать хранимую процедуру, которая принимает параметры start_date, end_date и список местоположений для включения. Сложность в том, что sys_run_hrs
может быть “сбрасываемо” (см. таблицу, где местоположение 2 сбрасывается между 01.03.2024 и 01.04.2024), и я хочу рассчитывать общие системные часы только в возрастающих диапазонах.
Пример:
location_id | status_date | sys_run_hrs |
---|---|---|
… | … | … |
2 | 2024-01-01 | 2 |
2 | 2024-01-02 | 3 |
2 | 2024-01-03 | 0 |
2 | 2024-01-04 | 3 |
7 | 2024-01-01 | 24 |
7 | 2024-01-02 | 25 |
7 | 2024-01-07 | 32 |
5 | 2024-01-03 | 4 |
… | … | … |
exec sys_run_hrs @start_date="2024-01-01", @end_date="2024-01-04", @locations="2;4";
-- Я хотел бы разделить список местоположений в хранимой процедуре по точкам с запятой, но открыт для других вариантов
Вывод:
location_id | total_hours |
---|---|
2 | 4 |
7 | 1 |
Спасибо!
Ниже представлено то, что мне предоставили, но я все еще не думаю, что это учитывает сброс часов.
declare
@l_StartDate datetimeoffset(3),
@l_EndDate datetimeoffset(3);
set @l_StartDate = CAST('2023-01-01 00:00:00.000 -05:00' as datetimeoffset(3));
select @l_EndDate = DATEADD(year, 1, @l_StartDate);
print @l_StartDate;
print @l_EndDate;
with run_hrs as
(
select distinct hb.LOCATION_ID,
l.location_name,
FIRST_VALUE(hb.SYS_RUN_HRS) OVER (
PARTITION BY hb.location_id
ORDER BY hb.STATUS_DATE ) AS sys_run_hours_Start,
LAST_VALUE(hb.SYS_RUN_HRS) OVER (
PARTITION BY hb.location_id
ORDER BY hb.STATUS_DATE
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS sys_run_hours_End
from history_table hb
inner join locations l on l.LOCATION_ID = hb.LOCATION_ID
where hb.STATUS_DATE >= @l_StartDate and hb.STATUS_DATE < @l_EndDate
and l.ACTIVE_INDC = 1
and l.LOCATION_ID in (28, 196, 38, 1478, 1474, 1469, 1468)
)
select run_hrs.LOCATION_ID,
run_hrs.LOCATION_NAME,
run_hrs.sys_run_hours_start,
run_hrs.sys_run_hours_end,
(run_hrs.sys_run_hours_end - run_hrs.sys_run_hours_start) as Run_hours
from run_hrs
order by LOCATION_NAME
Ответ или решение
Для решения проблемы подсчета общего количества часов работы системы, когда часы могут быть сброшены, мы можем создать хранимую процедуру, которая будет учитывать указанный диапазон дат и перечисленные локации. Основная задача здесь – правильно группировать и суммировать часы между сбросами, а также обрабатывать переданные параметризированные входные данные.
Шаг 1: Создание хранимой процедуры
В этой процедуре мы будем использовать временные таблицы и оконные функции для вычисления всех системных часов, исключая часы после сброса.
CREATE PROCEDURE sys_run_hrs
@start_date DATETIME,
@end_date DATETIME,
@locations VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
-- Преобразование переменной @locations в таблицу для использования в запросе
DECLARE @LocationsTable TABLE (location_id INT);
INSERT INTO @LocationsTable(location_id)
SELECT value
FROM STRING_SPLIT(@locations, ';')
WHERE TRY_CAST(value AS INT) IS NOT NULL;
WITH RunHours AS (
SELECT
hb.location_id,
hb.status_date,
hb.sys_run_hrs,
ROW_NUMBER() OVER (PARTITION BY hb.location_id ORDER BY hb.status_date) AS rn
FROM history_table hb
WHERE hb.status_date BETWEEN @start_date AND @end_date
AND hb.location_id IN (SELECT location_id FROM @LocationsTable)
),
AggregatedRunHours AS (
SELECT
rh.location_id,
SUM(rh.sys_run_hrs) as total_hours,
COUNT(*) as entries
FROM RunHours rh
-- Идентифицируем сбросы. Сброс определяется как sys_run_hrs = 0
-- Мы отбираем только те записи, где есть последовательные значения, отличные от нуля
WHERE rh.sys_run_hrs > 0
GROUP BY
rh.location_id,
(SELECT SUM(CASE WHEN sys_run_hrs = 0 THEN 1 ELSE 0 END)
OVER (PARTITION BY rh.location_id ORDER BY rh.status_date ROWS UNBOUNDED PRECEDING CURRENT ROW))
)
SELECT
loc.location_id,
ISNULL(SUM(aru.total_hours), 0) AS total_hours
FROM @LocationsTable loc
LEFT JOIN AggregatedRunHours aru ON loc.location_id = aru.location_id
GROUP BY loc.location_id
ORDER BY loc.location_id;
END
Шаг 2: Объяснение логики
-
Разделение входных данных: Мы разбиваем строку с идентификаторами локаций (
@locations
) на отдельные значения с помощью функцииSTRING_SPLIT
и вставляем их во временную таблицу@LocationsTable
. Это позволяет нам легко обрабатывать список локаций. -
Выбор данных: В общем запросе
RunHours
мы выбираем все записи изhistory_table
, которые находятся в заданном диапазоне времени и принадлежат указанным локациям. -
Фильтрация нулевых значений: Мы фильтруем записи таким образом, чтобы учитывать только те, где
sys_run_hrs
больше нуля. Это позволяет нам игнорировать периоды, в которых часы были сброшены (то есть, когдаsys_run_hrs
равны нулю). -
Группировка и суммирование: В
AggregatedRunHours
мы группируем записи поlocation_id
и применяем суммирование, чтобы получить общее количество часов для каждой локации. -
Итерация и вывод: Наконец, мы возвращаем итоговые часы для каждой локации. В случае, если данных для локации нет, возвращаем 0.
Пример использования
Вызов процедуры будет производиться следующим образом:
EXEC sys_run_hrs '2024-01-01', '2024-01-04', '2;4';
Этот вызов вернет результаты, которые показывают общее количество системных часов для указанных локаций с учетом всех сбросов в заданном диапазоне дат.
Таким образом, мы можем эффективно подсчитывать часы работы системы в условиях, когда сбросы происходят в указанные промежутки времени.