Postgresql
總結以前日期的所有唯一值
假設,為簡單起見,我有下表:
id amount p_id date ------------------------------------------------ 1 5 1 2020-01-01T01:00:00 2 10 1 2020-01-01T01:10:00 3 15 2 2020-01-01T01:20:00 4 10 3 2020-01-01T03:30:00 5 10 4 2020-01-01T03:50:00 6 20 1 2020-01-01T03:40:00
這是我想要的範例響應:
{ "2020-01-01T01:00:00": 25, -- this is from adding records with ids: 2 and 3 "2020-01-01T03:00:00": 55 -- this is from adding records with ids: 3,4,5 and 6 }
我想得到按小時分組
sum(amount)
的所有 unique 的總數( ) 。p_id
每個選擇的行
p_id
是最新的date
。因此,例如,上述響應中的第一個值不包括在內id 1
,因為記錄id 2
具有相同p_id
且該date
行上的記錄較晚。一件棘手的事情是我想包括所有
amount
每個人的總和,p_id
如果他們date
在提出的時間之前。因此,例如,在響應的第二個值(鍵為“2020-01-01T03:00:00”)中,即使id 3
時間戳在不同的時間,它也是最新的,p_id 2
因此包含在總和中“2020-01-01T03:00:00”。但是具有相同id 6
覆蓋的行。id 2``p_id 1
換句話說:到目前為止,總是取
amount
每個最新的,併計算表中每個不同小時的總和。p_id
簡單案例
獲取單個給定小時的總和相對簡單:
SELECT timestamp '2020-01-01 03:00:00', sum(amount) FROM ( SELECT DISTINCT ON (p_id) amount FROM tbl WHERE date < timestamp '2020-01-01 03:00:00' + interval '1h' ORDER BY p_id, date DESC ) sub;
DISTINCT ON
通常比使用row_number()
. 看:對於每行的許多行
p_id
,有可能通過智能索引和相應的查詢進行更多優化。看:每小時特別流水
您似乎想要表中每個不同小時的總和。
您可以簡單地提取不同的時間,並將簡單的解決方案應用於
LATERAL
子查詢中的任何一個:WITH grid AS ( -- or some other source? SELECT DISTINCT date_trunc('hour', date) AS hour FROM tbl ) SELECT * FROM grid CROSS JOIN LATERAL ( SELECT sum(amount) FROM ( SELECT DISTINCT ON (p_id) amount FROM tbl WHERE date < grid.hour + interval '1h' ORDER BY p_id, date DESC ) sub1 ) sub2 ORDER BY grid.hour;
應該沒問題,雖然沒有很多不同的時間。但是查詢的擴展性不是很好。每增加一小時以全部(穩步增加)成本計算
amount
每一個的最新值。p_id
優化執行時間數小時
p_id
我希望這個遞歸 CTE 能夠更好地擴展(雖然最初有相當大的成本),因為它只需要每隔一小時加入一行:WITH RECURSIVE values AS ( SELECT DISTINCT ON (1, 2) date_trunc('hour', date) AS hour, p_id, amount FROM tbl ORDER BY 2, 1, date DESC -- ! ) , hours AS ( SELECT hour, row_number() OVER (ORDER BY hour) AS hnr FROM (SELECT DISTINCT hour FROM values) sub ) , cte AS ( SELECT p_id, h.hour, v.amount, 2 AS next_hnr FROM (SELECT DISTINCT p_id FROM values) i -- all IDs JOIN hours h ON hnr = 1 -- first hour LEFT JOIN values v USING (p_id, hour) UNION ALL SELECT c.p_id, h.hour, COALESCE(v.amount, c.amount), c.next_hnr + 1 FROM cte c JOIN hours h ON h.hnr = c.next_hnr LEFT JOIN values v ON v.p_id = c.p_id AND v.hour = h.hour ) SELECT hour, sum(amount) FROM cte GROUP BY 1 ORDER BY 1;
此外,根據價值分佈,可能還有更多的優化潛力,如頂部的簡單案例所示。
db<>在這裡擺弄
另外:將您的
timestamp
列稱為“日期”會產生誤導,因為這是一種不同的基本數據類型。