我有一张表来监控一些具有日期时间和状态的设备。我想计算"运行日"之间" RUN"和"停止"状态。 我尝试以下请求:
select run.stamp -
(
-- select the first STOP status after the current RUN status
select stamp
from well_monitoring
where stamp > run.stamp and status = 'STOP'
order by stamp limit 1
)
from well_monitoring run
where
run.status = 'RUN'
and ( -- we want only the first RUN
select status
from well_monitoring
where stamp < run.stamp
order by stamp desc limit 1) <> 'RUN'
order by run.stamp
请参阅SQLFiddle了解表创建/数据并测试请求。
当我尝试SUM以获得总运行天数时:
select SUM( run.stamp - ... ) ...
我有以下错误:
错误:列&#34; run.stamp&#34;必须出现在GROUP BY子句中或者是 用于聚合函数位置:448
所以: - 如何更新我的查询以获得总和? - 查询有2个子查询,有更好的方法(cte?)来做吗?
(Postgres版本:9.1.7)
答案 0 :(得分:1)
这可能会有点简化,但它有效(注意它确实需要窗口函数,所以它可以适应任何SQL实现)
SELECT one.id , one.stamp, one.status
, two.id, two.stamp, two.status
, (two.stamp - one.stamp) AS diff
FROM well_monitoring one
JOIN well_monitoring two ON two.well_id = one.well_id
AND two.stamp > one.stamp
AND two.status = 'STOP'
-- find the first STOP:
-- there should be on other STOP
-- between one: RUN
-- and two: STOP
AND NOT EXISTS (
SELECT * FROM well_monitoring x
WHERE x.well_id = one.well_id
AND x.stamp > one.stamp
AND x.stamp < two.stamp
AND x.status = 'STOP'
)
WHERE one.status = 'RUN'
-- If there are consecutive RUNs
-- (without an intervening STOP)
-- one should be the first RUN
AND NOT EXISTS (
SELECT * FROM well_monitoring x
WHERE x.well_id = one.well_id
AND x.status = 'RUN'
AND x.stamp < one.stamp
AND NOT EXISTS (
SELECT * FROM well_monitoring xx
WHERE xx.well_id = x.well_id
AND xx.stamp > x.stamp
AND xx.stamp < one.stamp
AND xx.status <> 'RUN'
)
)
;
添加聚合作为练习留给读者。
答案 1 :(得分:1)
这与@ joop的答案大致相同,但使用窗口函数进行边缘检测。请注意,需要使用rank + not exists()来将停止事件与最近的开始事件配对。
WITH edges AS (
SELECT id AS this_id
, status AS this_status
, well_id AS well_id
, LAG(status) over ww AS prev_status
, dense_rank() over ww AS rnk
FROM well_monitoring
WINDOW ww AS (partition by well_id ORDER BY stamp)
)
, starters AS (
SELECT this_id, well_id, rnk
FROM edges
WHERE this_status = 'RUN'
AND COALESCE(prev_status, 'OMG') <> 'RUN'
)
, stoppers AS (
SELECT this_id, well_id, rnk
FROM edges
WHERE prev_status = 'RUN'
AND this_status <> 'RUN'
)
SELECT m0.well_id
, SUM(m1.stamp - m0.stamp)::interval AS duration
FROM starters s0
JOIN stoppers s1 ON s1.well_id = s0.well_id
AND s1.rnk > s0.rnk
AND NOT EXISTS (
SELECT * FROM stoppers nx
WHERE nx.well_id = s0.well_id
AND nx.rnk > s0.rnk AND nx.rnk < s1.rnk
)
JOIN well_monitoring m0 ON m0.id = s0.this_id
JOIN well_monitoring m1 ON m1.id = s1.this_id
GROUP BY m0.well_id
;
结果:
well_id | duration
---------+-------------------
1 | 320 days 64:28:00
(1 row)
(我怀疑64小时是一个错误...)
答案 2 :(得分:0)
您应该使用以下命令包装查询:
select sum(times) as sum_of_times
from (
select run.stamp -
(
-- select the first STOP status after the current RUN status
select stamp
from well_monitoring
where stamp > run.stamp and status = 'STOP'
order by stamp limit 1
) times
from well_monitoring run
where
run.status = 'RUN'
and ( -- we want only the first RUN
select status
from well_monitoring
where stamp < run.stamp
order by stamp desc limit 1) <> 'RUN'
order by run.stamp
) alias
就像这个sqlfiddle。
在这个sqlfiddle中,您可以看到如何使用CTE,以防您希望在一个结果集中拥有所有时间和总和。