簡體   English   中英

SQL:從任意間隔計算月平均值

[英]SQL: calculate monthly averages from arbitrary intervals

我有一個日志表,以以下形式存儲事件

timestamp,        object_id, state
2018-08-12 13:45  123        10
2018-08-13 15:56  183        25
2018-08-13 15:58  123        10
2018-08-15 16:02  256        15

有一個主鍵(為簡便起見不包括在內),時間戳是日期時間字段,object_id是與不同表的前鍵關系,狀態是0-100范圍內的整數。 事件是在事件發生時記錄下來的,事件之間的狀態不一定會改變,因此同一object_id可能具有多個連續的具有相同狀態的記錄。

數據庫是PostgreSQL 9.5

我要做的是計算單個對象或通過某些條件選擇的對象的每月,每天和每周間隔的平均狀態。 我期望每日平均的結果看起來像

date,        object_id, average state
2018-08-12   123        18.6
2018-08-13   123        37.1
2018-08-14   123        126.7
2018-08-15   123        5.5

其中平均狀態是根據對象在間隔中(在上述情況下,一天中)在一分鍾間隔內在每個給定狀態中花費的時間量加權得出的,因此,如果對象在狀態10中花費了23小時,而在狀態10中花費了15分鍾狀態50,平均值應為

15/1440 * 50 + 1425/1440 * 10 = 10.42

到目前為止,我已經設法使用窗口函數將各個事件轉換為狀態更改之間的間隔。 SQL看起來像這樣

SELECT
    state.object_id,
    state.timestamp as start, 
    lead(timestamp) OVER (ORDER BY timestamp) as end,
    state.state, 
FROM 
(
    SELECT 
        *, 
        rank() OVER (PARTITION BY (state) ORDER BY timestamp)
    FROM event_log AS l
    WHERE object_id=123 AND timestamp >= DATE '2018-01-01'
) AS state
WHERE state.rank=1
ORDER BY timestamp

並獲得輸出,該輸出為我提供了狀態實際改變時間隔的開始和結束。 我不確定從這里去哪里。 這些事件並不總是頻繁發生,因此我的間隔可能會持續三天,並且我需要以某種方式每天進行報告,因此我需要將該間隔分成幾天。 我該如何正確處理呢?

好的,一種計算該平均值的方法是使用generate_series()實際展開所有分鍾,然后通過子查詢將狀態分配給它們,然后使用GROUP BY ID和day進行分配。

SELECT date_trunc('day',
                  "gs"."timestamp") "date",
       "x1"."object_id",
       avg((SELECT "el1"."state"
                   FROM "event_log" "el1"
                   WHERE "el1"."object_id" = "x1"."object_id"
                         AND "el1"."timestamp" <= "gs"."timestamp"
                   ORDER BY "el1"."timestamp" DESC
                   LIMIT 1)) "state"
       FROM (SELECT "el1"."object_id",
                    min(date_trunc('minute',
                                   "el1"."timestamp")) "timestamp_begin",
                    max(date_trunc('minute',
                                   "el1"."timestamp")) "timestamp_end"
                    FROM "event_log" "el1"
                    GROUP BY "el1"."object_id") "x1"
             CROSS JOIN LATERAL generate_series("x1"."timestamp_begin",
                                                "x1"."timestamp_end",
                                                '1 minute'::interval) "gs"("timestamp")
       GROUP BY date_trunc('day',
                           "gs"."timestamp"),
                "x1"."object_id"
       ORDER BY date_trunc('day',
                           "gs"."timestamp"),
                "x1"."object_id";

分貝<>小提琴

結果:

date                | object_id |               state
:------------------ | --------: | ------------------:
2018-08-12 00:00:00 |       123 | 10.0000000000000000
2018-08-13 00:00:00 |       123 | 10.0000000000000000
2018-08-13 00:00:00 |       183 | 25.0000000000000000
2018-08-15 00:00:00 |       256 | 15.0000000000000000

這個想法是生成對象的第一個時間戳和最后一個時間戳之間的所有分鍾。 然后將最新的已知狀態分配給分鍾,該狀態在該分鍾之前或該分鍾之前記錄。

如果我們有每一分鍾和一個狀態,那么它是一個或多或少簡單的聚合查詢,以獲取每天和每個對象的平均值。

首先,我們獲得每個對象的精確到分鍾的第一個和最后一個時間戳,子查詢的別名為"x1" 為了將時間戳截斷為分鍾精度,我們使用date_trunc()

我們通過generate_series()橫向交叉連接"x1" ,並在第一分鍾和最后一分鍾將其送入。 這將生成從第一個到最后一個分鍾的時間戳。

現在,在avg()調用的子查詢中,我們選擇所有行,其中對象與外部查詢中的當前行相同,並且時間戳小於或等於當前行之一。 但是我們只想要這些中的最新版本。 因此,我們按時間戳對它們進行降序排序,從排序后的第一個中選擇一個。

我們再次使用date_trunc()現在將分鍾截斷為幾天,並按它們和對象分組。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM