如何声明变量并使用它们根据前面的行更新每一行

问题描述 投票:0回答:1

我正在循环浏览

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$;

数据库<>小提琴

sql postgresql plpgsql window-functions dynamic-sql
1个回答
0
投票

是否有更好的方法来使用这些变量执行更新,而不将命令转换为字符串?

听起来你在问

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 随机行的测试中:

  • 您的初始函数需要
    23.4s
  • 加上
    execute..using
    的运行效果大致相同,
    23.9
  • 允许
    update
    读取先前的值,直接将其削减为
    10.9s
  • 上面的普通 SQL
    update
    只需要
    3.7s
© www.soinside.com 2019 - 2024. All rights reserved.