sql - Postgres 中的时间序列查询

标签 sql postgresql time-series greatest-n-per-group generate-series

这是@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 列表中的表一样使用集合返回函数。

SQL Fiddle

一个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/

相关文章:

python - 如何突出显示图中的区域以指示 Python 中的滑动窗口?

javascript - 为涉及 DataTypes.JSON 的 Sequelize 创建种子文件时出错

mysql - 为什么 SQL 声明失败?

mysql - 从依赖于连接表的子查询中选择求和值

python - 如何重新采样具有应用于每列的不同函数的数据框?

pandas - pandas 六个月的日期范围

c# - 索引(从零开始)必须大于或等于零

php - Laravel groupBy 无法与 PostgreSQL 正常工作

django - 使用单独模式的 Django 和 Postgresql 分层 Multi-Tenancy 架构

java - PostgresQL中如何使用SCRAM-SHA-256通过jdbc连接数据库