我正在使用Postresql 9.6并试图找出如何根据不同列的离散总和(在同一个表中)创建查询来计算列的总和。例如,我想要每组行的计数总和,直到音量> = 100,然后再开始计算计数总和。
示例数据:
id count volume
1 5 12
2 6 14
3 2 11
4 10 9
5 5 14
6 17 19
7 0 8
8 12 4
9 18 6
10 12 14
11 10 10
12 15 7
13 8 12
14 2 17
15 5 30
16 9 24
17 2 16.5
假设的中间结果(只是为了理解我的期望):
id sum(count) discrete volume
1 5 12
2 11 26
3 13 37
4 23 46
5 28 60
6 45 79
7 45 87
8 57 91
9 75 97
10 87 111
11 10 10 (reset since volume >= 100)
12 25 17
13 33 29
14 35 46
15 40 76
16 49 100
17 2 16.5 (reset since volume >= 100)
预期的最终结果:
sum(count) discrete volume
87 111
49 100
2 16.5 (partial result, which is desired)
到目前为止我尝试过:SQL Fiddle
我对来自SQL Fiddle的another StackOverflow answer中显示的当前查询有所了解,这显然无法正常工作(但是我正在努力解决我试图解决的其他问题)。我从different StackOverflow answer看到我可能想要使用RECURSIVE查询(PostgreSQL Documentation)但我无法弄清楚如何正确编写查询以便它可以工作:(
我当然可以用Java代码编写这个(并且已经有),但是我想在SQL中这样做,所以希望比读取所有行和计算结果更快。我也很可能编写一个存储过程来使这项工作,但我宁愿避免这种情况,因为我正在使用JPA(在这种情况下使用本机查询)并希望将所有代码保存在同一个地方( Java代码库)。此外,我希望能够动态包含/排除列,所以我想用Java代码构造查询(实际表中的列比我的示例多得多)。
感谢您提供任何帮助。
修改
感谢@klin的评论并查看引用的StackOverflow问题,我越来越近了。这是我的(生成中间结果):
WITH RECURSIVE WorkTable(id, count_sum, volume_sum) AS
(
SELECT
id,
count AS count_sum,
volume AS volume_sum
FROM measurements
WHERE id = 1
UNION ALL
SELECT
measurements.id,
CASE WHEN WorkTable.volume_sum >= 100
THEN measurements.count
ELSE WorkTable.count_sum + measurements.count
END AS count_sum,
CASE
WHEN WorkTable.volume_sum >= 100
THEN measurements.volume
ELSE WorkTable.volume_sum + measurements.volume
END AS discrete_sum_volume
FROM measurements
JOIN WorkTable
ON measurements.id = WorkTable.id + 1
)
SELECT *
FROM WorkTable
ORDER BY id
然而,我仍然缺少的是如何获得最终结果。如果我使用WHERE volume_sum >= 100
,我不会得到最终(部分)结果。我不能使用OR id = MAX(id)
,因为Postgres不允许在WHERE子句中使用它。
编辑: BTW在@klin和@JorgeCampos花了大量时间查看并回答我的问题之后,我发现此查询不会对包含数百万行的表执行。我已经创建了一个存储过程;我不想去那里,但似乎没有其他表现良好的选择。对于大型表,存储过程会以多个数量级执行RECURSIVE查询。
答案 0 :(得分:3)
我使用row_count()
因为需要连续的行号。您不应该依赖主键,因为它通常可能包含间隙。
with recursive cte as (
select *, row_number() over (order by id)
from measurements
),
work_table as (
select
id,
count as count_sum,
volume as volume_sum,
row_number
from cte
where row_number = 1
union all
select
c.id,
case when w.volume_sum >= 100
then c.count
else w.count_sum + c.count
end as count_sum,
case
when w.volume_sum >= 100
then c.volume
else w.volume_sum + c.volume
end as discrete_sum_volume,
c.row_number
from cte c
join work_table w
on c.row_number = w.row_number + 1
)
select count_sum, volume_sum
from work_table
where volume_sum >= 100
or id = (select max(id) from work_table)
order by id
结果:
count_sum | volume_sum
-----------+------------
87 | 111
49 | 100
2 | 16.5
(3 rows)