【问题标题】:Grouping aggregate by day按天分组聚合
【发布时间】:2017-10-22 19:44:34
【问题描述】:

我在 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}}]

...这是多余的(并且可能效率低下)。获取按天分组的结果的正确方法是什么,这一天只出现在它自己的列中,而操作聚合只针对那一天?

【问题讨论】:

  • 什么 postgres 版本?
  • @Mihai 我正在运行 9.6

标签: sql json postgresql aggregate-functions


【解决方案1】:

使用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)

【讨论】:

    【解决方案2】:

    您可以先将该行转换为 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
    

    【讨论】:

      【解决方案3】:

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

      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 } |
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-04
        • 2021-04-29
        • 2014-05-29
        • 1970-01-01
        • 2015-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多