【问题标题】:How to adapt this query to use window functions如何调整此查询以使用窗口函数
【发布时间】:2013-12-11 17:34:26
【问题描述】:

当我开始处理这个问题时,我想,“这将是一个很好的学习窗口函数的查询。”我最终无法让它与窗口函数一起使用,但我能够使用连接获得我想要的。

您将如何调整此查询以使用窗口函数:

SELECT
    day,
    COUNT(i.project) as num_open
FROM generate_series(0, 364) as t(day)
    LEFT JOIN issues i on (day BETWEEN i.closed_days_ago AND i.created_days_ago)
GROUP BY day
ORDER BY day;

上面的查询获取范围由 created_days_ago 和 closed_days ago 表示的问题列表,并且在过去 365 天内,它将计算该特定日期已创建但尚未关闭的问题数量。

http://sqlfiddle.com/#!15/663f6/2

issues 表如下所示:

CREATE TABLE issues (
  id SERIAL,
  project VARCHAR(255),
  created_days_ago INTEGER,
  closed_days_ago INTEGER);

我的想法是,给定日期的分区应该包括问题中的所有行,其中日期介于创建日期和关闭日期之间。类似SELECT day, COUNT(i.project) OVER (PARTITION day BETWEEN created_days_ago AND closed_days_ago) ...

我以前从未使用过窗口函数,所以我可能遗漏了一些基本的东西,但似乎这正是使窗口函数如此出色的查询类型。

【问题讨论】:

  • 我认为窗口不会对此有用。您需要generate_series 来创建行集。窗口函数不能生成全新的行,因此您不能使用窗口函数为零问题的那些日子创建行,或者为没有打开或关闭问题的那些日子生成一行。
  • "created_days_ago 整数" -- 哎哟。您实际上运行了一个 cron 来每天增加这个值,而不是存储时间戳或日期? :-(
  • @Denis,它们实际上作为日期存储在实际表中,返回的是视图中计算的 days_ago 值。我只是为这个问题简化了一点。
  • +1 用于展示一个完整的案例(针对一个有趣的问题),包括一个工作小提琴。仅缺少您的 Postgres 版本。

标签: sql postgresql aggregate-functions window-functions cumulative-sum


【解决方案1】:

您使用generate_series() 创建完整的日期范围,包括没有变化的日期,因此表issues 中没有行, 排除使用窗口函数。

事实上,在我的本地测试中,这个查询比 Q 中的查询运行 50 倍:

SELECT t.day
      ,  COALESCE(sum(a.created) OVER (ORDER BY t.day DESC), 0)
       - COALESCE(sum(b.closed)  OVER (ORDER BY t.day DESC), 0) AS open_tickets
FROM   generate_series(0, 364) t(day)
LEFT   JOIN (SELECT created_days_ago AS day, count(*) AS created
             FROM   issues GROUP BY 1) a USING (day)
LEFT   JOIN (SELECT closed_days_ago AS day, count(*) AS closed
             FROM   issues GROUP BY 1) b USING (day)
ORDER  BY 1;

这也是正确,与问题中的查询相反,它导致第 0 天有 17 张未处理的票,尽管所有票都已关闭。
该错误是由于您的连接条件中的BETWEEN 造成的,其中包括上 下边框。这样,门票在关闭当天仍然算作“打开”。

结果中的每一行都反映了一天结束时打开的票数

解释

查询结合窗口函数和聚合函数。

  • 子查询a 计算每天创建的工单数量。这导致每天只有一行,使其余的更容易。
    子查询 b 对已关闭的工单执行相同的操作。

  • 使用 LEFT JOIN 加入子查询 t 中生成的天数列表。
    小心加入多个未聚合的表!这可能会在每行多个匹配的联接表中触发CROSS JOIN,从而产生不正确的结果。比较:
    Two SQL LEFT JOINS produce incorrect result

  • 最后使用两个窗口函数来计算已创建票证和已关闭票证的运行总数。
    另一种方法是在外部 SELECT

    中使用它
    sum(COALESCE(a.created, 0)
      - COALESCE(b.closed,  0)) OVER (ORDER BY t.day DESC) AS open_tickets
    

    在我的测试中表现相同。

-> SQLfiddle demo.

除此之外:我永远不会在表中存储“days_ago”,而是绝对日期/时间戳。看起来像这个问题的简化。

【讨论】:

  • 好点。我将问题视为“如何使用原始表上的窗口替换此连接”-您实际上无法做到这一点,但是正如您所展示的,使用运行仍然可以解决更好的问题总计。
  • @CraigRinger:我假设您的意思是“更好的 查询”。
  • 呃。是的。一个更好的查询。我责怪早上。
  • 太棒了!正是我想要帮助我理解窗口功能的东西。谢谢。
  • @StevePrentice:很好。我添加了更多建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 2021-09-04
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
相关资源
最近更新 更多