按天分组聚合

时间:2017-10-22 19:44:34

标签: sql json postgresql aggregate-functions

我在Postgres数据库中有一个简单的表log,看起来像这样(示例简化):

time::timestamptz
action::varchar

time                          | action
2017-10-16 17:14:49.473903-08 | 'some action'
2017-10-16 17:14:49.473903-08 | 'some other action'
2017-10-17 17:14:49.473903-08 | 'some action'
//etc.

有各种各样的行动。我想查询此表以获得每天一行,其中包含每个操作的计数数组。这是我想要的结果:

day            actions
'2017-10-08' | [{"action":"some action", "count":10}, 
             |  {"action":"some other action", "count":20}}]
'2017-10-09' | [{"action":"some action", "count":15}, 
             |  {"action":"some other action", "count":18}}]

我几乎能够实现这一目标:

SELECT day, json_agg(act) as actions
FROM (
    SELECT action, time::date as day,  COUNT(*)
    FROM log_hits
    GROUP BY (action, day)
) act
GROUP BY day

当然,这会导致在actions数组中的每个对象中出现日期......

day           actions
'2017-10-08' | [{"action":"some action", day:"2017-10-08", "count":10}, 
             |  {"action":"some other action",  day:"2017-10-08", "count":20}}]

......这是多余的(可能效率低下)。获取按日分组的结果的正确方法是什么,日期只发生在自己的列中,而操作仅聚合当天?

3 个答案:

答案 0 :(得分:5)

使用jsonb_build_object():

WITH log_hits (time, action) AS (
VALUES
    ('2017-10-16 17:14:49.473903-08'::timestamptz, 'some action'),
    ('2017-10-16 17:14:49.473903-08', 'some other action'),
    ('2017-10-17 17:14:49.473903-08', 'some action')
)

SELECT 
    day, 
    json_agg(jsonb_build_object('action', action, 'count', count)) as actions
FROM (
    SELECT action, time::date as day, COUNT(*)
    FROM log_hits
    GROUP BY (action, day)
) act
GROUP BY day;

    day     |                                       actions                                        
------------+--------------------------------------------------------------------------------------
 2017-10-17 | [{"count": 1, "action": "some action"}, {"count": 1, "action": "some other action"}]
 2017-10-18 | [{"count": 1, "action": "some action"}]
(2 rows)

答案 1 :(得分:1)

您可以先将行转换为jsonb,然后删除day

SELECT day, json_agg(row_to_jsonb(act) - 'day') as actions
FROM (
    SELECT action, time::date as day,  COUNT(*)
    FROM log_hits
    GROUP BY action, day
) act
GROUP BY day

答案 2 :(得分:1)

不完全是您要求的结果,但无论如何都要张贴,因为有人可能会觉得它更合适:

SELECT day, json_object_agg("action","count") as actions
FROM (
    SELECT action, ts::date as day, COUNT(*) as count
    FROM log_hits
    GROUP BY (action, ts::date) ORDER BY count DESC
) act
GROUP BY day ORDER BY day

|        day |                                        actions |
|------------|------------------------------------------------|
| 2017-10-16 | { "some action" : 2, "some other action" : 1 } |
| 2017-10-17 |                          { "some action" : 1 } |
| 2017-10-18 |                          { "some action" : 1 } |