0

我有一个大约 800 万行的表,我需要从中获取时间片数据。
我正在使用 PostgreSQL 9.1。

我需要每天查询此表以获取每个“object_id”(给定列表)的 max(start_time) 关联“数据”值的总和。(换句话说,对于特定列表中的每个 object_id,最接近每天结束的记录)。

这是基本的表结构:

CREATE TABLE checks (
  id SERIAL PRIMARY KEY,
  object_id INTEGER,
  state INTEGER,
  start_time TIMESTAMP,
  data TEXT
);

data是一个TEXT字段,但具有numeric值(我无法更改此方面,但可以使用强制转换进行转换)。

这是我目前正在使用的查询:

WITH object_ids AS ( 
    SELECT object_id FROM objects WHERE object_id in (14845,12504,12451,12452)
),
records AS (
    SELECT
        data,
        start_time,
        MAX(start_time) OVER (PARTITION BY object_id)
    FROM checks
    WHERE
        object_id IN (SELECT object_id FROM object_ids) AND
        state = 0 AND
        start_time BETWEEN '2013-05-01 00:00:00' AND '2013-05-02 00:00:00'
)   
SELECT 
    SUM(data::bigint) 
FROM   
    records
WHERE 
    max = start_time

我将为每个月的每一天运行此查询,以提供一组图表数据点。

我很想修改这个查询,这样我就不必每天运行单独的查询,而是一个查询返回一组每天的值

start_time          | sum
---------------------------
2013-05-01 00:00:00 | 39118
2013-05-02 00:00:00 | 98387
2013-05-03 00:00:00 | 8384

我一直在研究时间片问题,它们非常有帮助(我将使用窗口函数的事实归功于 StackOverflow!),但我无法解决这个问题。

4

2 回答 2

1
SELECT day, sum(data) AS total_per_day
FROM  (
   SELECT DISTINCT ON (object_id, 1)
          start_time::date, data::numeric
   FROM   checks c
   WHERE  object_id in (14845,12504,12451,12452)
   AND    state = 0
   AND    start_time >= '2013-04-01'::date
   AND    start_time <  '2013-05-05'::date   -- any range of days
   ORDER  BY object_id, 1, c.start_time DESC -- seems redundant, see text
   ) x
GROUP BY  1
ORDER BY  1

这给了你一个总和的一天。我意识到这很像@Clodoaldo 已经发布的内容,但我演示了正确的DISTINCT ON语法,以及一些其他改进和一些必要的解释。

  • 您可以使用DISTINCT ON每天的数据值。应该比窗口函数更简单、更快:

  • ORDER BY子句必须与DISTINCT ON表达式一致(链接答案中的详细信息)。这就是原因:

    ORDER  BY 1, start_time::date, start_time DESC
    

    第二项似乎是多余的,但在这里需要。

  • date从 a中获取timestamp,只需强制转换:start_time::date

  • 当您在列表中包含start_time::date(我优化了)时要小心SELECTGROUP BY输入列和ORDER BY输出列优先于输入列(与WHEREHAVING只能引用输入列相反)。您必须为输出列使用不同的别名或对基列进行表限定以引用它:c.start_time

  • timestamp你几乎总是会想要排除上边界。此相关答案中的详细信息:

于 2013-05-08T01:52:17.557 回答
0
select
    "day", sum(data) "data"
from (
    select distinct (1, 2)
        object_id,
        date_trunc('day', start_time)::date "day",
        start_time,
        "data"
    from checks
    where
        object_id in (14845,12504,12451,12452)
        and state = 0
    order by 1, 2, 3 desc
) s
group by 1
order by 1
于 2013-05-07T23:22:26.190 回答