这是@Erwin 对 Efficient time series querying in Postgres 的回答的后续问题.
为了简单起见,我将使用与该问题相同的表结构
id | widget_id | for_date | score |
最初的问题是获取范围内每个日期的每个小部件的分数。如果某个日期没有小部件条目,则显示该小部件上一个条目的分数。如果所有数据都包含在您查询的范围内,则使用交叉连接和窗口函数的解决方案效果很好。我的问题是我想要以前的分数,即使它位于我们正在查看的日期范围之外。
示例数据:
INSERT INTO score (id, widget_id, for_date, score) values
(1, 1337, '2012-04-07', 52),
(2, 2222, '2012-05-05', 99),
(3, 1337, '2012-05-07', 112),
(4, 2222, '2012-05-07', 101);
当我查询 2012 年 5 月 5 日至 5 月 10 日的范围时(即 generate_series('2012-05-05'::date, '2012-05-10'::date, '1d')
) 我想得到以下内容:
DAY WIDGET_ID SCORE
May, 05 2012 1337 52
May, 05 2012 2222 99
May, 06 2012 1337 52
May, 06 2012 2222 99
May, 07 2012 1337 112
May, 07 2012 2222 101
May, 08 2012 1337 112
May, 08 2012 2222 101
May, 09 2012 1337 112
May, 09 2012 2222 101
May, 10 2012 1337 112
May, 10 2012 2222 101
目前最好的解决方案(也是@Erwin)是:
SELECT a.day, a.widget_id, s.score
FROM (
SELECT d.day, w.widget_id
,max(s.for_date) OVER (PARTITION BY w.widget_id ORDER BY d.day) AS effective_date
FROM (SELECT generate_series('2012-05-05'::date, '2012-05-10'::date, '1d')::date AS day) d
CROSS JOIN (SELECT DISTINCT widget_id FROM score) AS w
LEFT JOIN score s ON s.for_date = d.day AND s.widget_id = w.widget_id
) a
LEFT JOIN score s ON s.for_date = a.effective_date AND s.widget_id = a.widget_id
ORDER BY a.day, a.widget_id;
但正如您在这个 SQL Fiddle 中看到的那样它在前两天为小部件 1337 生成空分数。我希望看到第 1 行中较早的分数 52 代替它。
有没有可能以一种有效的方式做到这一点?
最佳答案
作为@Roman mentioned , DISTINCT ON
可以解决这个问题。此相关答案中的详细信息:
不过,子查询通常比 CTE 快一点:
SELECT DISTINCT ON (d.day, w.widget_id)
d.day, w.widget_id, s.score
FROM generate_series('2012-05-05'::date, '2012-05-10'::date, '1d') d(day)
CROSS JOIN (SELECT DISTINCT widget_id FROM score) AS w
LEFT JOIN score s ON s.widget_id = w.widget_id AND s.for_date <= d.day
ORDER BY d.day, w.widget_id, s.for_date DESC;
您可以像 FROM
列表中的表一样使用集合返回函数。
一个multicolumn index应该是性能的关键:
CREATE INDEX score_multi_idx ON score (widget_id, for_date, score)
第三列 score
仅包含在内以使其成为 covering index in Postgres 9.2 or later .您不会将其包含在早期版本中。
当然,如果您有很多小部件和很宽的天数,CROSS JOIN
会产生很多行,这有一个价格标签。仅选择您实际需要的小部件和日期。
关于sql - Postgres 中的时间序列查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19442362/