我正在循环浏览
table1
:
create table table1(id,"date",quantity,"value")as values
(1,'2024-10-01',1,1)
,(2,'2024-10-02',1,1)
,(3,'2024-10-03',1,1)
,(4,'2024-10-04',1,1)
,(5,'2024-10-05',1,1)
,(6,'2024-10-06',1,1)
,(7,'2024-10-07',1,1);
并使用变量
value
中存储的值更新列_previous_value
(我需要计算循环中下一行的值)。
是否有更好的方法来使用这些变量执行更新,而不将命令转换为字符串?
类似:
UPDATE table1 SET value = {_previous_value} WHERE table1.id = {_r.id};
我知道我可以使用
LAG()
来获取上一行的值,但在这种特定情况下我需要该函数。它按原样工作,我只是想知道是否有一种“正确”的方法可以在不连接和 EXECUTE
命令的情况下执行此操作。
CREATE OR REPLACE FUNCTION functiontest()
RETURNS VOID LANGUAGE plpgsql AS $f$
DECLARE _r RECORD;
DECLARE _previous_value DOUBLE PRECISION := 0;
BEGIN
FOR _r IN
SELECT table1.date, table1.id
FROM table1
ORDER BY table1.date, table1.id
LOOP
WITH c AS
( SELECT
table1.id,
table1.quantity + _previous_value AS total
FROM table1
WHERE table1.id = _r.id
ORDER BY table1.date, table1.id
LIMIT 1
)
SELECT
COALESCE(c.total, 0)
INTO
_previous_value
FROM c;
EXECUTE
'UPDATE table1 SET value = ' || _previous_value ||
' WHERE table1.id = ' || _r.id || ';';
END LOOP;
END $f$;
是否有更好的方法来使用这些变量执行更新,而不将命令转换为字符串?
execute..using
:db<>fiddle的demo1
EXECUTE $dsql$ UPDATE table1
SET value = $1
WHERE table1.id = $2 ;
$dsql$ USING _previous_value, _r.id;
您也可以让
update
直接从select
获取数据:demo2 at db<>fiddle
WITH c AS(
SELECT table1.id
, table1.quantity + _previous_value AS total
FROM table1
WHERE table1.id = _r.id
ORDER BY table1.date
, table1.id
LIMIT 1
),updated as(
UPDATE table1
SET value = COALESCE(c.total, 0)
FROM c
WHERE table1.id = c.id
RETURNING value)
SELECT value
FROM updated
INTO _previous_value;
read committed
事务隔离模式,因此除非您添加某种 锁定,否则如果并发客户端决定 insert into
/update
/delete from
,您可能会看到异常情况
当您正在循环遍历该表时。
如果您在一个简单的 SQL 查询中一次性运行这个
update
,那么所有这些令人头痛的问题都会消失:demo3 at db<>fiddle
with new_state as (
select id
, sum(quantity)over(order by date,id) new_value
from table1)
update table1
set value=new_value
from new_state
where table1.id=new_state.id;
PL/pgSQL 中的循环虽然直观,但通常比纯声明式 SQL 中的循环性能差得多,因此这也会显着加快速度。在 200k 随机行的测试中: