user_id | date | distance
1 | 2019-04-09 00:00:00 | 2
1 | 2019-04-09 00:00:30 | 5
1 | 2019-04-09 00:01:00 | 3
1 | 2019-04-09 00:01:45 | 7
1 | 2019-04-09 00:02:30 | 6
1 | 2019-04-09 00:03:00 | 1
如何求和下一行的总和,直到达到阈值点,然后再次重置计数器。
例如,如果阈值为10,我正在尝试获得以下输出:
1 | 2019-04-09 00:00:00 | 2
1 | 2019-04-09 00:00:30 | 7 (2 + 5)
1 | 2019-04-09 00:01:00 | 10 ( 7 + 3 )
1 | 2019-04-09 00:01:45 | 7 RESET
1 | 2019-04-09 00:02:30 | 13 (7 + 6 )
1 | 2019-04-09 00:03:00 | 1 RESET
但是我所能实现的是通过以下查询获得累积距离:
SELECT *, sum(distance) over (order by date asc) as running_distance FROM table;
我正在使用PostgreSQL。
答案 0 :(得分:2)
实时测试:http://sqlfiddle.com/#!17/16716/2
SELECT *, sum_with_reset(distance, 10) over (order by date asc) as running_distance
FROM tbl;
用户定义的汇总sum_with_reset定义:
create or replace function sum_reset_accum(
_accumulated numeric, _current numeric, _threshold numeric
)
returns numeric as
$$
select case when _accumulated >= _threshold then
_current
else
_current + _accumulated
end
$$ language sql;
create aggregate sum_with_reset(numeric, numeric)
(
sfunc = sum_reset_accum,
stype = numeric,
initcond = 0
);
数据
CREATE TABLE tbl
("user_id" int, "date" timestamp, "distance" int)
;
INSERT INTO tbl
("user_id", "date", "distance")
VALUES
(1, '2019-04-09 00:00:00', 2),
(1, '2019-04-09 00:00:30', 5),
(1, '2019-04-09 00:01:00', 3),
(1, '2019-04-09 00:01:45', 7),
(1, '2019-04-09 00:02:30', 6),
(1, '2019-04-09 00:03:00', 1)
;
输出:
| user_id | date | distance | running_distance |
|---------|----------------------|----------|------------------|
| 1 | 2019-04-09T00:00:00Z | 2 | 2 |
| 1 | 2019-04-09T00:00:30Z | 5 | 7 |
| 1 | 2019-04-09T00:01:00Z | 3 | 10 |
| 1 | 2019-04-09T00:01:45Z | 7 | 7 |
| 1 | 2019-04-09T00:02:30Z | 6 | 13 |
| 1 | 2019-04-09T00:03:00Z | 1 | 1 |
单线:
create or replace function sum_reset_accum(
_accumulated numeric, _current numeric, _threshold numeric
)
returns numeric as
$$
select _current + _accumulated * (_accumulated < _threshold)::int
$$ language 'sql';
通过使用强制转换运算符::int
,Postgres布尔值可以将true转换为1,将false转换为0。
您也可以使用plpgsql
语言:
create or replace function sum_reset_accum(
_accumulated numeric, _current numeric, _threshold numeric
)
returns numeric as
$$begin
return _current + _accumulated * (_accumulated < _threshold)::int;
end$$ language 'plpgsql';
请注意,您无法在sqlfiddle.com上创建plpgsql函数,因此无法在sqlfiddle.com上测试该plpgsql代码。您可以,但是在您的计算机上。
答案 1 :(得分:1)
作为变体,您可以使用RECURSIVE CTE
来获取
测试表:
CREATE TABLE public.table_b (
user_id int4 NULL,
"date" timestamp NULL,
distance int4 NULL
);
INSERT INTO public.table_b (user_id,"date",distance) VALUES
(1,'2019-04-09 00:00:00.000',2)
,(1,'2019-04-09 00:00:30.000',5)
,(1,'2019-04-09 00:01:00.000',3)
,(1,'2019-04-09 00:01:45.000',7)
,(1,'2019-04-09 00:02:30.000',6)
,(1,'2019-04-09 00:03:00.000',1)
,(2,'2019-04-09 00:00:00.000',2)
,(2,'2019-04-09 00:00:30.000',5)
,(2,'2019-04-09 00:01:00.000',3)
,(2,'2019-04-09 00:01:45.000',7)
,(2,'2019-04-09 00:02:30.000',6)
,(2,'2019-04-09 00:03:00.000',1);
查询:
WITH RECURSIVE cte1 AS(
SELECT *,ROW_NUMBER()OVER(PARTITION BY user_id ORDER BY date) n
FROM table_b
),
cte2 AS(
SELECT user_id,date,distance,distance running_distance,n
FROM cte1
WHERE n=1
UNION ALL
SELECT c1.user_id,c1.date,c1.distance,CASE WHEN c2.running_distance<10 THEN c2.running_distance ELSE 0 END+c1.distance running_distance,c1.n
FROM cte1 c1
JOIN cte2 c2 ON c2.user_id=c1.user_id AND c2.n+1=c1.n
)
SELECT user_id,date,distance,running_distance
FROM cte2
ORDER BY user_id,date