Группировка данных за 10 секунд. PLSQL - PullRequest
1 голос
/ 30 сентября 2019

Необходимо определить среднее количество обработанных сообщений абонентом за 10 секунд в течение часа. PLSQL Есть столбцы: подписчик, дата ('dd.mm.yyyy hh.mm.ss') и сообщения, они связаны (у каждого сообщения есть свое время и подписчик, который его обработал).

theИдея заключалась в следующем:

SELECT subscriber, HH24, AVG(CNT) FROM (
SELECT subscriber,
trunc(date, 'HH24') HH24,
trunc(date - INTERVAL '10' SECOND) SS,
count(messages) CNT
FROM tables
where date IS NOT NULL
GROUP BY subscriber,
trunc(date, 'HH24'),
trunc(date - INTERVAL '10' SECOND)
order by subscriber)
GROUP BY subscriber, HH24

Ожидаемый результат:

subscriber          HH24          CNT   
subscriber 1   01.01.01 21:00:00  8,88
subscriber 1   01.01.01 22:00:00  7,88
subscriber 2   01.01.01 21:00:00  6,66
subscriber 3   01.01.01 22:00:00  5,54

Мой запрос производит что-то вроде следующего:

subscriber         HH24          CNT
subscriber 1  01.01.01 21:00:00  400
subscriber 1  01.01.01 22:00:00  500
subscriber 2  01.01.01 21:00:00  300
subscriber 3  01.01.01 22:00:00  500

Скорее всего, мой запрос не группируетданные за 10 секунд, подскажите где я ошибся? Спасибо!

Ответы [ 3 ]

0 голосов
/ 30 сентября 2019

Не ясно, что вы ищете. Для такого рода задач я использую эту универсальную функцию:

CREATE OR REPLACE FUNCTION MakeInterval(ts IN TIMESTAMP, roundInterval IN INTERVAL DAY TO SECOND) RETURN TIMESTAMP DETERMINISTIC IS
    denom INTEGER;
BEGIN
    IF roundInterval >= INTERVAL '1' HOUR THEN
        denom := EXTRACT(HOUR FROM roundInterval);
        IF MOD(24, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts) + TRUNC(EXTRACT(HOUR FROM ts) / denom) * denom * INTERVAL '1' HOUR;
    ELSIF roundInterval >= INTERVAL '1' MINUTE THEN
        denom := EXTRACT(MINUTE FROM roundInterval);
        IF MOD(60, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts, 'hh') + TRUNC(EXTRACT(MINUTE FROM ts) / denom) * denom * INTERVAL '1' MINUTE;
    ELSE
        denom := EXTRACT(SECOND FROM roundInterval);                
        IF MOD(60, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts, 'mi') + TRUNC(EXTRACT(SECOND FROM ts) / denom) * denom * INTERVAL '1' SECOND;
    END IF;
END MakeInterval;

Затем вы можете использовать ее просто как MakeInterval("date", INTERVAL '10' SECOND)

Однако, возможно, вы ищете windowing_clause например

AVG(CNT) OVER (PARTITION BY subscriber ORDER BY "date" RANGE BETWEEN CURRENT_ROW AND INTERVAL '10' SECOND FOLLOWING)
0 голосов
/ 02 октября 2019

Я плохо объяснил, но проблема была решена следующим образом:

SELECT subscriber, HH24, AVG(cnt / 3600) * 100 / 10 cnt  

AVG - среднее значение, CNT / 3600 - деление на количество секунд в час, * 100 - перевод в процентах, /10 - получаем ответ за 10 секунд

0 голосов
/ 30 сентября 2019

Пожалуйста, попробуйте это:

select subscriber, ss, count(сообщения) cnt from (
select subscriber,
       trunc(date) + (floor((date-trunc(date))*24*60*60/10)*10)/24/60/60 as ss, 
       сообщения
from tables
where date is not null)
group by subscriber, ss;

Я бы лучше объяснил большое выражение:

(date-trunc(date))*24*60*60  -- Time expressed as number of seconds since midnight (T1)
(floor(T1/10)*10)            -- ... rounded down to nearest 10 seconds (T2)
T2/24/60/60                  -- ... converted to fraction of a day (T3)
trunc(date)+T3               -- ... added back on to date
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...