问题描述
我想在 WHILE
循环中运行 UPSERT。这是我的查询:
do $$ declare start_date date := '2021-01-16';
begin
while start_date::date < '2021-03-01' loop
insert
into
A ( id,"year","month",movement,status)
(
select
id,date_year,date_month,s_movement,move_status
from
B
where
date_year = extract(year
from
start_date::date)
and date_month = (extract(month
from
start_date::date)+ 1)
and date_day = extract(day
from
start_date::date))
on conflict (id) do
update set status = move_status,movement = s_movement;
start_date:=start_date+interval '1 day';
end loop;
end $$
sql Error [42703]: ERROR: column "move_status" does not exist Hint:
There is a column named "move_status" in table "*SELECT*",but it cannot be referenced from this part of the query.
如何解决?
解决方法
错误的直接原因是您尝试引用输入列名称,但在 UPSERT 的 UPDATE
部分中只有目标列名称可见。并且您必须使用 virtual table name EXCLUDED
对这些进行表限定。
但还有更多。使用基于 generate_series()
的基于集合的解决方案,而不是 DO
命令中的循环。效率更高:
INSERT INTO A
(id,year,month,movement,status)
SELECT id,date_year,date_month,s_movement,move_status
FROM generate_series(timestamp '2021-01-16',timestamp '2021-02-28',interval '1 day') start_date
JOIN B ON date_year = extract(year FROM start_date)
AND date_month = extract(month FROM start_date) + 1
AND date_day = extract(day FROM start_date)
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status -- here!,movement = EXCLUDED.movement; -- and here!
旁白:考虑用一个类型为 date
的列来替换三列 date_year
、date_month
、date_day
。更干净、更高效。
进一步阅读: