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

时间:2013-12-11 17:34:26

标签: sql postgresql aggregate-functions window-functions running-total

当我开始解决这个问题时,我想,“这将是一个很好的查询来了解窗口函数。”我无法最终使用窗口函数,但我能够通过连接得到我想要的东西。

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

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表示范围的问题列表,在过去的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) ...

这样的东西

我以前从未使用过窗口函数,所以我可能会遗漏一些基本的东西,但看起来这只是使窗口函数如此棒的查询类型。

1 个答案:

答案 0 :(得分:2)

使用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”,而是绝对日期/时间戳。看起来像是为了这个问题的简化。