我有一个名为rain_tanks的Postgres表,如下所示:
id hour rain demand current_volume unmet_demand
1 00:00 4.0 2.0 2 0.0
2 00:10 3.0 4.0 [null] [null]
3 00:20 1.0 6.0 [null] [null]
4 00:30 7.0 3.0 [null] [null]
我想进行此计算并更新current_volume和unmet_demand列(此代码仅用于显示需要完成的操作。我想在不使用Python中的函数或代码行的情况下执行此操作):
a = lag(current_volume) + rain - demand
if a < 0:
unmet_demand = current_volume
current_volume = 0
else:
unmet_demand = 0
current_volume = a
预期表格:
id hour rain demand current_volume unmet_demand
1 00:00 4.0 2.0 2 0
2 00:10 3.0 4.0 1 0
3 00:20 1.0 6.0 0 -4
4 00:30 7.0 3.0 4 0
我想我需要的是首先选择SELECT和UPDATE列。我为SELECT尝试了以下内容,但它不起作用:
import psycopg2 as p
conn = p.connect("dbname = 'test' user = 'postgres' password = 'pass' host = 'localhost'")
cur = conn.cursor()
cur.execute("""SELECT Id,rain,demand,current_volume,unmet_demand,
CASE WHEN (rain - demand + lag(current_volume) over(
order by Id)) >= 0
THEN (rain - demand + lag(current_volume) over(
order by Id)) ELSE 0 END
FROM rain_tanks ORDER BY Id""")
任何帮助都会非常感激。
编辑(添加与性能相关的问题):我决定在postgres数据库中进行这些计算的原因是看看与在python中使用numpy数组相比是否有速度提升。我有大约1000万点的雨和需求列,这里提供的答案需要比同等数量的python函数更长的时间比雨量和需求量大。还有空间来提高查询的性能吗?答案 0 :(得分:0)
您应该使用递归cte来获得所需的结果。由于每行取决于之前的计算,而lag
无法实现。
WITH RECURSIVE CTE(id,rain,demand,new_current_volume,new_unmet_demand) as
(SELECT Id,rain,demand
,case when rain-demand <0 then 0 else rain-demand end as new_current_volume
,case when rain-demand <0 then rain-demand else 0 end as new_unmet_demand
FROM rain_tanks
WHERE id = 1
UNION ALL
SELECT r2.Id,r2.rain,r2.demand
,case when r1.new_current_volume+r2.rain-r2.demand <0 then 0 else r1.new_current_volume+r2.rain-r2.demand end
,case when r1.new_current_volume+r2.rain-r2.demand <0 then r1.new_current_volume+r2.rain-r2.demand else 0 end
FROM cte r1
JOIN rain_tanks r2 ON r2.id=r1.id+1
)
SELECT * FROM CTE;
编辑:
根据新计算的值向update
表格添加2个null
列,其中包含表格中的列名称以及计算出的列数。然后,您可以在cte。
update
WITH RECURSIVE CTE(id,rain,demand,current_volume,unmet_demand,new_current_volume,new_unmet_demand) as
(SELECT Id,rain,demand,null,null
,case when rain-demand <0 then 0 else rain-demand end as new_current_volume
,case when rain-demand <0 then rain-demand else 0 end as new_unmet_demand
FROM rain_tanks
WHERE id = 1
UNION ALL
SELECT r2.Id,r2.rain,r2.demand,null,null
,case when r1.new_current_volume+r2.rain-r2.demand <0 then 0 else r1.new_current_volume+r2.rain-r2.demand end
,case when r1.new_current_volume+r2.rain-r2.demand <0 then r1.new_current_volume+r2.rain-r2.demand else 0 end
FROM cte r1
JOIN rain_tanks r2 ON r2.id=r1.id+1
)
UPDATE rain_tanks r
SET current_volume=c.new_current_volume
,unmet_demand=c.new_unmet_demand
FROM cte c
WHERE r.id=c.id;