"distinct on" 按 postgres 分组

"distinct on" with group by postgres

我有以下记录:

id  run_hour               performance_hour      value
2  "2017-06-25 09:00:00"  "2017-06-25 07:00:00"    6
2  "2017-06-25 09:00:00"  "2017-06-25 08:00:00"    5
1  "2017-06-25 09:00:00"  "2017-06-25 08:00:00"    5
2  "2017-06-25 08:00:00"  "2017-06-25 07:00:00"    5
1  "2017-06-25 08:00:00"  "2017-06-25 07:00:00"    5

我们 运行 每小时查看当前小时和前几个小时的每个 id 的结果。

仅当与前一小时相比发生变化时 运行 我们才插入新记录 (我们不想覆盖该值,因为我们想测量 1 小时或 2 小时等后的值。

我想对每个 id 的最新可用值求和(按 run_hour 排序)- 值。

在上面的示例中,运行 9:00 的广告 1 和 7:00 的表演时间没有记录 - 因为它与 运行 的 8:00 和 7:00

的表演时间

在上面的例子中,如果我要求 运行 2017-06-25 09:00:00 的总和,我希望得到:

id, value
1   10
2   11

对于id 1,计算出来的是10:(run_hour<2017-06-25 08:00:00> + run_hour<2017-06-25 09:00:00>) 对于 id 2,它是 11 计算出来的:(run_hour<2017-06-25 09:00:00> + run_hour<2017-06-25 09:00:00>) 我写了以下查询:

select distinct on (id, run_hour) id, sum(value) from metrics where  run_hour <= '2017-06-25 09:00' and performance_hour >= '2017-06-25 07:00' and  performance_hour < '2017-06-25 09:00'
group by id
order by id, run_hour

但是我得到一个错误,run_hour 也必须在 GROUP BY 子句中。 - 但如果我添加它,我会得到不正确的数据 - 还有我不需要的前几小时的数据 - 我需要有数据的最新一小时。

如何将 "distinct on" 与分组依据一起使用?

你想要 distinct on 之前 group by:

select id, sum(value)
from (select distinct on (id, run_hour) m.*
      from metrics m
      where run_hour <= '2017-06-25 09:00' and
            performance_hour >= '2017-06-25 07:00' and
            performance_hour < '2017-06-25 09:00'
      order by id, run_hour, performance_hour desc
     ) m
group by id;

任务很复杂。假设您希望从以下数据中获得 7:00 到 9:00 的表演时间:

id  run_hour               performance_hour      value
2   "2017-06-25 09:00:00"  "2017-06-25 06:00:00"    6
2   "2017-06-25 09:00:00"  "2017-06-25 10:00:00"    5

预期结果为 18(7:00 为 6 + 8:00 为 6 + 9:00 为 6)全部基于本身位于外部的 6:00 记录所需的时间范围。

我们需要一个递归的 CTE,从每个 id 的第一个想要的表演时间开始,直到最后一个想要的表演时间。因此我们建立了不存在的记录,我们可以稍后总结。

with recursive cte(id, run_hour, performance_hour, value) as
(
  select *
  from
  (
    select distinct on (id) 
      id, 
      run_hour,
      greatest(performance_hour, timestamp '2017-06-25 07:00') as performance_hour, 
      value
    from metrics
    where run_hour = timestamp '2017-06-25 09:00' 
      and performance_hour <= timestamp '2017-06-25 07:00'
    order by id, metrics.performance_hour desc
  ) start_by_id
  union all
  select 
    cte.id, 
    cte.run_hour,
    cte.performance_hour + interval '1 hour' as performance_hour,
    coalesce(m.value, cte.value) as value
  from cte
  left join metrics m on m.id = cte.id
                      and m.run_hour = cte.run_hour
                      and m.performance_hour = cte.performance_hour + interval '1 hour'
  where cte.performance_hour < timestamp '2017-06-25 09:00'
)
select id, sum(value)
from cte
group by id;

Rextester link:http://rextester.com/PHC88770