【问题标题】:Get count of created entries for each day获取每天创建的条目数
【发布时间】:2015-06-04 10:09:26
【问题描述】:

假设我有一个这样的搜索查询:

SELECT COUNT(id), date(created_at)
FROM entries
WHERE date(created_at) >= date(current_date - interval '1 week')
GROUP BY date(created_at)

如你所知,例如我得到这样的结果:

count | date
  2   |  15.01.2014
  1   |  13.01.2014
  9   |  09.01.2014

但我确实没有得到一周中没有创建条目的日子。

我怎样才能得到一个看起来像这样的搜索结果,包括没有创建条目的日子?

count | date
  2   |  15.01.2014
  0   |  14.01.2014
  1   |  13.01.2014
  0   |  12.01.2014
  0   |  11.01.2014
  0   |  10.01.2014
  9   |  09.01.2014

【问题讨论】:

    标签: sql postgresql aggregate-functions generate-series


    【解决方案1】:
    SELECT day, COALESCE(ct, 0) AS ct
    FROM  (SELECT now()::date - d AS day FROM generate_series (0, 6) d) d  -- 6, not 7
    LEFT   JOIN (
       SELECT created_at::date AS day, count(*) AS ct 
       FROM   entries
       WHERE  created_at >= date_trunc('day', now()) - interval '6d'
       GROUP  BY 1
       ) e USING (day);
    
    • WHERE 条件使用sargable 表达式,这样Postgres 可以在created_at 上使用普通索引。性能比其他所有因素都重要。

    • 要涵盖一周(包括今天),请从“今天”的开头减去 6 天,而不是 7。

    • 假设id 定义为NOT NULLcount(*) 与此处的count(id) 相同,但速度稍快。

    • CTE 在这里会有点矫枉过正。它更慢更冗长。

    • 先聚合,后加入。在这种情况下会更快。

    • now() 是标准 SQL CURRENT_TIMESTAMP(您也可以使用)的更短和更快的 Postgres 实现。

    这应该是最短和最快的查询。使用EXPLAIN ANALYZE 进行测试。

    相关:

    【讨论】:

    • 感谢您的回答!如果它有效,我会将其标记为正确,但目前我收到一个错误:FEHLER: Unteranfrage in FROM muss Aliasnamen erhalten ZEILE 2: FROM (SELECT now::date - d AS day FROM generate_series (0, ...
    • 我通过使用current date 修复了它并为表格提供了一个别名!谢谢
    • @Erwin Brandstetter,count* 在一列上比 count 快吗?在聚合函数中选择*超过一列时,行宽不会变大吗?
    • @ConsiderMe: 当在count(*) 中使用* 时,它实际上并没有分解成整行,它只是代表行本身:仅仅存在一行就足够了,实际value 不用检查,这样会快一点。
    • @ErwinBrandstetter 你是对的。使用*PK column 进行测试。支持* 略有不同。再来一次,谢谢。
    【解决方案2】:

    使用generate_series() 创建您需要的日期并加入此日期列表:

    SELECT  COUNT(id), 
        date(gdate)
    FROM entries
        RIGHT JOIN generate_series(current_date - interval '1 week', current_date, '1 day') g(gdate) 
        ON date(created_at) = date(gdate) AND date(created_at) >= date(current_date - interval '1 week')
    GROUP BY 
        date(gdate)
    ORDER BY
        date(gdate) DESC;
    

    【讨论】:

    • @JohnSmith:你遇到了什么样的错误/问题?应该是这样的,但我没有测试过。
    • 我得到的结果和以前一样!
    • WHERE 条件必须移动到 JOIN 条件。我用固定日期替换了“current_date”,我的笔记本电脑已经在使用 March....
    • 哦,这个太棒了!如果您更正查询以使用 current_date,我会将其评为正确答案!谢谢!
    【解决方案3】:

    试试这个查询:

    with a as (select current_date - n as dt from generate_series(0, 6) as t(n)),
         b as (select count(id) cnt, date(created_at) created_at
               from entries
               where date(created_at) >= date(current_date - interval '1 week')
               group by date(created_at))
    select coalesce(b.cnt,0), a.dt
    from a
    left join b on (a.dt = b.created_at)
    order by a.dt;
    

    count 函数不会为不存在的行生成 0。因此,您必须填写缺失日期的行。使用generate_series 和简单的日期算法,您可以为某个时期(在本例中为 1 周)的日期生成行。然后你可以外连接来生成最终结果。 coalesce 将替换 null0

    【讨论】:

    • 有效!但是有什么更短的吗?
    • 您可以删除 COALESCE(),COUNT(column) 不计算 NULL 值。 COUNT(*) 计算记录并且不检查 NULL。
    【解决方案4】:

    您需要告诉 SQL 处理 NULL。如果NULL,则返回0

    您可以通过COALESCE 完成此操作

    http://www.postgresql.org/docs/devel/static/functions-conditional.html

    【讨论】:

    • 谢谢!但我不明白它完全可以你添加一个查询,具体到我的问题吗?
    • 不存在的日期不会使用 COALESCE 弹出。这根本行不通。
    猜你喜欢
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2020-07-18
    • 2020-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多