2017-01-12 2 views
0

Скажем, у меня есть таблица activities с полями starttime (TIMESTAMP) и stoptime (TIMESTAMP). Я хочу найти момент, в течение которого происходит большинство действий. Запрос должен вернуться в первый такой момент.Google BigQuery - самый активный момент на основе временного интервала

Я попытался получить все отметки времени starttime, а затем для каждого из них подсчитайте количество действий, которые происходят в этот момент. Затем найти максимум:

#standardSQL 
SELECT 
    time, 
    (
    SELECT COUNT(*) 
    FROM activities 
    WHERE starttime <= time AND time <= stoptime 
) AS cnt 
FROM (
    SELECT DISTINCT starttime AS time 
    FROM activities 
    ORDER BY time 
) 
ORDER BY cnt DESC, time ASC 
LIMIT 1 

К сожалению, он говорит: LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join.

Я думаю, что правильный алгоритм для этой внешней базы данных мира было бы получить все starttimes и stoptimes поместить их в массив таким образом, что они будет отличаться, сортировать его, а затем последовательно следовать этим массивом в поисках максимального момента. Однако я понятия не имею, как выразить такой алгоритм в SQL.

Я видел this, но я не думаю, что это помогает.

+0

что зернистость ваш момент - это второй , минуту или час или что-то еще? –

+0

@MikhailBerlyant Я думаю, что это миллисекунды. –

+0

, так что вам нужно найти, на какой ровно миллисекунду в течение общего периода времени вы получили большинство действий? пожалуйста, подтвердите, так как это звучит непрактично для большинства случаев использования, но у вас может быть специальный случай –

ответ

2

Я достиг чего-то близкого к алгоритму, который я описал в вопросе. Он работает прилично быстро, но если вы найдете что-нибудь лучше, я был бы рад увидеть его.

#standardSQL 
SELECT time, SUM(add) OVER(ORDER BY time ASC, add DESC) AS cumsum 
FROM (
    SELECT starttime AS time, 1 AS add 
    FROM activities UNION ALL 
    SELECT stoptime AS time, -1 AS add 
    FROM activities 
) 
ORDER BY cumsum DESC 
1

Рассмотрим ниже версии
С моей точки зрения, это возвращает более практичное вывод - что - все периоды (соответствующее начало и конец) последовательной деятельности одного и того же уровня
Так что вы теперь не только начинается но целый период (начало и конец) с наивысшей активностью. а не только один, но все они

#standardSQL 
WITH intervals AS (
    SELECT time AS start_, LEAD(time) OVER(ORDER BY time) AS end_ 
    FROM (
    SELECT DISTINCT time FROM (
     SELECT starttime AS time FROM activities UNION ALL 
     SELECT stoptime AS time FROM activities)) 
), 
equals AS (
    SELECT start_, end_, COUNT(1) AS cumsum 
    FROM intervals AS i 
    JOIN activities AS a 
    ON i.start_ >= a.starttime AND i.end_ <= a.stoptime 
    GROUP BY start_, end_ 
), 
grps AS (
    SELECT 
    start_, end_, cumsum, 
    IFNULL(
     CAST(end_ = LEAD(start_) OVER(ORDER BY start_) AND LEAD(cumsum) OVER(ORDER BY start_) = cumsum AS INT64), 
     CAST(NOT((start_ = LAG(end_) OVER(ORDER BY start_) AND LAG(cumsum) OVER(ORDER BY start_) = cumsum)) AS INT64) 
    ) AS flag 
    FROM equals 
) 
SELECT MIN(start_) AS start_, MAX(end_) AS end_, cumsum 
FROM (
    SELECT start_, end_, cumsum, SUM(flag) OVER(ORDER BY start_) AS grp 
    FROM grps 
) 
GROUP BY cumsum, grp 
ORDER BY start_ 

вы можете играть с выше, с использованием манекена деятельность Таблица

WITH activities AS (
    SELECT 1 AS starttime, 3 AS stoptime UNION ALL 
    SELECT 1 AS starttime, 4 AS stoptime UNION ALL 
    SELECT 4 AS starttime, 5 AS stoptime UNION ALL 
    SELECT 7 AS starttime, 8 AS stoptime UNION ALL 
    SELECT 7 AS starttime, 10 AS stoptime UNION ALL 
    SELECT 8 AS starttime, 12 AS stoptime 
) 

или

WITH activities AS (
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 1 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 3 MINUTE) AS stoptime UNION ALL 
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 1 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 4 MINUTE) AS stoptime UNION ALL 
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 4 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 5 MINUTE) AS stoptime UNION ALL 
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 7 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 8 MINUTE) AS stoptime UNION ALL 
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 7 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 10 MINUTE) AS stoptime UNION ALL 
    SELECT TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 8 MINUTE) AS starttime, TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 12 MINUTE) AS stoptime 
) 
Смежные вопросы