当我开始解决这个问题时,我想,“这将是一个很好的查询来了解窗口函数。”我无法最终使用窗口函数,但我能够通过连接得到我想要的东西。
您如何调整此查询以使用窗口函数:
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) ...
我以前从未使用过窗口函数,所以我可能会遗漏一些基本的东西,但看起来这只是使窗口函数如此棒的查询类型。
答案 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
在我的测试中执行相同的操作。
除此之外:我永远不会在表中存储“days_ago”,而是绝对日期/时间戳。看起来像是为了这个问题的简化。