7

我有以下查询来计算每分钟的所有数据。

$sql= "SELECT COUNT(*) AS count, date_trunc('minute', date) AS momento
FROM p WHERE fk_id_b=$id_b GROUP BY date_trunc('minute', date) 
ORDER BY momento ASC";

我需要做的是得到每行的计数与过去 2 分钟的计数的总和。

For example with the result of the $sql query above
|-------date---------|----count----|
|2012-06-21 05:20:00 |      12     |
|2012-06-21 05:21:00 |      14     |
|2012-06-21 05:22:00 |      10     |
|2012-06-21 05:23:00 |      20     |
|2012-06-21 05:24:00 |      25     |
|2012-06-21 05:25:00 |      30     |
|2012-06-21 05:26:00 |      10     |  

I want this result:

|-------date---------|----count----|
|2012-06-21 05:20:00 |      12     |   
|2012-06-21 05:21:00 |      26     |     12+14
|2012-06-21 05:22:00 |      36     |     12+14+10
|2012-06-21 05:23:00 |      44     |     14+10+20
|2012-06-21 05:24:00 |      55     |     10+20+25
|2012-06-21 05:25:00 |      75     |     20+25+30
|2012-06-21 05:26:00 |      65     |     25+30+10
4

3 回答 3

12

lag()窗口函数(也在SQL Fiddle上)不是那么棘手:

CREATE TABLE t ("date" timestamptz, "count" int4);
INSERT INTO t VALUES
('2012-06-21 05:20:00',12),
('2012-06-21 05:21:00',14),
('2012-06-21 05:22:00',10),
('2012-06-21 05:23:00',20),
('2012-06-21 05:24:00',25),
('2012-06-21 05:25:00',30),
('2012-06-21 05:26:00',10);

SELECT *,
    "count"
    + coalesce(lag("count", 1) OVER (ORDER BY "date"), 0)
    + coalesce(lag("count", 2) OVER (ORDER BY "date"), 0) AS "total"
  FROM t;
  1. 我已经双引号datecount列,因为这些是保留字;
  2. lag(field, distance)给我远离当前行的field列行的值,因此第一个函数给出前一行的值,第二个调用给出前一行的值;distance
  3. coalesce()需要避免函数的NULL结果lag()(对于查询中的第一行,没有“前一个”行,因此它是NULL),否则total也将是NULL.
于 2012-06-29T20:28:31.203 回答
12

这是当前行和前 N 行的值总和的更通用的解决方案(在您的情况下为 N = 2)。

SELECT "date", 
sum("count") OVER (order by "date" ROWS BETWEEN 2 preceding AND current row)
FROM t
ORDER BY "date";

您可以在 0 和“无界”之间更改 N。这种方法让您有机会在您的应用程序“过去 N 分钟的计数”中拥有一个参数。此外,如果超出范围,则无需处理默认值。

您可以在 PostgreSQL 文档 ( 4.2.8. Window Function Calls )中找到更多相关信息

于 2012-10-30T14:20:21.273 回答
9

@vyegorov 的回答主要涵盖了它。但我的抱怨多于评论。

  1. 根本不要使用像和这样的保留字作为标识符PostgreSQL 允许这两个特定的关键字作为标识符——除了每个 SQL 标准。但这仍然是不好的做法。您可以使用双引号内的任何内容作为标识符这一事实,甚至不是一个好主意。最重要的是,a的名称具有误导性。datecount"; DELETE FROM tbl;""date"timestamp

  2. 错误的数据类型。示例显示timestamp,不是timestamptz。在这里没有什么区别,但仍然具有误导性。

  3. 你不需要COALESCE()。使用窗口函数lag()lead()您可以提供默认值作为第三个参数:

基于此设置:

CREATE TABLE tbl (ts timestamp, ct int4);
INSERT INTO tbl VALUES
  ('2012-06-21 05:20:00', 12)
, ('2012-06-21 05:21:00', 14)
, ('2012-06-21 05:22:00', 10)
, ('2012-06-21 05:23:00', 20)
, ('2012-06-21 05:24:00', 25)
, ('2012-06-21 05:25:00', 30)
, ('2012-06-21 05:26:00', 10);

询问:

SELECT ts, ct + lag(ct, 1, 0) OVER (ORDER BY ts)
              + lag(ct, 2, 0) OVER (ORDER BY ts) AS total
FROM   tbl;

或者更好:使用单个sum()作为窗口聚合函数和自定义窗口框架

SELECT ts, sum(ct) OVER (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM   tbl;

结果相同。
有关的:

于 2012-06-29T21:14:01.323 回答