Совокупное среднее значение SQL по отдельному значению столбца (отметке времени)

#sql #google-bigquery #sum #partition

# #sql #google-bigquery #сумма #раздел

Вопрос:

У меня есть таблица SQL в следующем виде:

Результат timestamp user_id
1 125 1
1 125 0
2 125 1
2 144 0
3 156 1
3 125 1

и т.д….

Если я хочу найти скользящее среднее значение результатов пользователей, я могу использовать:

 SELECT user_id, timestamp, 
AVG(result) OVER (PARTITION BY user_id ORDER BY timestamp ASC ROWS UNBOUNDED PRECEDING) 
FROM table
 

Если у пользователя был только один результат на метку времени, это работает нормально, но если у пользователя есть несколько результатов в одной метке времени, он будет усредняться по результатам на этой метке времени плюс метки времени перед ним.

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

Заранее спасибо

Ответ №1:

По сути, вам нужен другой столбец, чтобы разорвать связи, например, первичный ключ таблицы, например, — в противном случае одноранговые строки рассматриваются все вместе в среднем по окну. Предполагая id :

 select t.*
    avg(result) over(partition by user_id order by timestamp, id) as avg_result
from mytable t
 

Обратите внимание, что rows спецификация фрейма здесь не нужна (как в вашем исходном запросе).

Если у вас нет такого столбца, мы можем сгенерировать его с row_number() помощью , что гарантирует, что каждому номеру присваивается только один раз. Однако результат не является согласованным, то есть при наличии связей одной и той же строке может быть присвоен другой номер строки во время последовательного выполнения одного и того же запроса:

 select t.*
    avg(result) over(partition by user_id order by timestamp, rn) as avg_result
from (
    select t.*,
        row_number() over(partition by user_id order by timestamp) as rn
    from mytable t
) t
 

Комментарии:

1. Я согласен со всем этим, но, похоже, это не работает — оно по-прежнему усредняется по строкам, которые имеют одинаковую временную метку, вместо того, чтобы усреднять только те, у которых временная метка меньше текущей временной метки. — Я использую Google BigQuery, так что, может быть, именно поэтому?

Ответ №2:

Как насчет этого?

 SELECT [timestamp]
,   [user_id]
,   [result]
,   [final_avg] = (
        CONVERT(FLOAT, SUM([result]) OVER(PARTITION BY [user_id], [timestamp] ORDER BY [timestamp], [user_id], [result] ROWS UNBOUNDED PRECEDING))
        / COUNT([timestamp]) OVER(PARTITION BY [user_id], [timestamp] ORDER BY [timestamp], [user_id], [result] RANGE UNBOUNDED PRECEDING))
FROM table
ORDER BY [timestamp], [user_id], [result]
 

Результат:

 timestamp   user_id result  final_avg
1           125     0       0
1           125     1       0.5
2           125     1       1
2           144     0       0
3           125     1       1
3           156     1       1
 

Комментарии:

1. Привет, Марк, спасибо за ответ, к сожалению, это не сработает — вторая строка должна игнорировать первую в скользящем подсчете, поскольку временная метка одинакова

2. Нет проблем. Какого результата вы хотите достичь? Например, каким будет ожидаемый результат final_avg для первых трех строк в таблице результатов выше?

3. Конечный результат будет: final_avg: null, null, 0.5, null, 0.666666, null. Единственное среднее значение для примера будет 125, поскольку для остальных это первая запись, спасибо за вашу помощь!

Ответ №3:

Вы можете использовать спецификацию рамки окна. В стандартном SQL это было бы:

 SELECT user_id, timestamp, 
       AVG(result) OVER (PARTITION BY user_id
                         ORDER BY timestamp ASC
                         RANGE BETWEEN UNBOUNDED PRECEDING AND INTERVAL '1 SECOND' PRECEDING
                        ) 
FROM table;
 

Редактировать:

В BigQuery вы бы сделали:

Это стандартная функциональность SQL, но не все базы данных поддерживают ее.

 SELECT user_id, timestamp, 
       AVG(result) OVER (PARTITION BY user_id
                         ORDER BY UNIX_SECONDS(timestamp) ASC
                         RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
                        ) 
FROM table;
 

Предполагается, что секунды являются достаточной степенью детализации для различения предыдущих времен.

Комментарии:

1. Привет, Гордон, спасибо за ответ — к сожалению, я подаю в суд на Bigquery, и он не поддерживает это