【发布时间】:2020-08-29 13:02:24
【问题描述】:
我试图使用在几行上运行的update 语句的SET 子句分配一个局部变量(实际上是两个)。好的,我正在做这个 ala MySQL。
drop table if exists stocks cascade;
create table stocks (
id serial,
stock_available int,
stock_locked int
);
insert into stocks(stock_available, stock_locked) values
(150, 10),
(150, 20),
(150, 0),
(100, 0),
(100, 100),
(100, 30),
(100, 0),
(100, 50),
(100, 0);
create or replace function lock_all ()
returns int
language plpgsql as $$
declare
_amount int;
_total int;
begin
-- initialize accumulator
_total = 0;
-- update all the stocks table rows
update stocks
set _amount = stock_available,
stock_locked = stock_locked + _amount,
_total = _total + _amount;
-- returns the units locked
return _total;
end;
$$;
不幸的是,这不是 PostgreSQL 期望的方式。
SQL Error [42703]: ERROR: column "_amount" of relation "stocks" does not exist
Where: PL/pgSQL function lock_all() line 10 at SQL statement
这只是一个简单的例子来说明计算/总结update 句子中更新的事物数量的真正问题。我确信这个特定示例可能有技巧或方法,但我对这种必须计算累加器的情况的通用解决方案感兴趣。
有什么想法吗?
编辑
根据@GMB 的建议,我链接了 3 个 ctes
create or replace function lock_all3 ()
returns int
language sql as $$
with
cte1 as (
select
sum(stock_locked)::int as initially_locked
from
stocks
),
cte2 as (
update
stocks
set
stock_locked = stock_locked + stock_available,
stock_available = 0
returning
0 as dummy
),
cte3 as (
select
sum(stock_locked)::int as finally_locked
from
stocks
)
select
(cte3.finally_locked - initially_locked - dummy)
from
cte1, cte2, cte3;
$$;
这应该可行,但结果值表明两个选择都是在表 stocks 的初始值上执行的,因为差异为 0。
select lock_all3();
lock_all3|
---------|
0|
但是,cte2 被执行,因为最终情况表明所有可用的股票都被锁定了。
select * from stocks;
id|stock_available|stock_locked|
--|---------------|------------|
1| 0| 160|
2| 0| 170|
3| 0| 150|
4| 0| 100|
5| 0| 200|
6| 0| 130|
7| 0| 100|
8| 0| 150|
9| 0| 100|
这个近似值肯定有问题。
【问题讨论】:
-
所有
CTEs 在 Postgresql 中总是使用原始数据快照,真正的数据更改是在执行结束时完成的。所以如果你想让第二个 CTE 看到第一个的结果,那么第一个必须带有 RETURNING,第二个必须从第一个中选择。 -
谢谢!我不知道
标签: postgresql local-variables postgresql-12