我正在尝试在 pgAdmin 中编写 Postgres 过程。我编写的过程失败,并显示不同的错误消息,具体取决于我将光标放在哪里。有时它甚至似乎成功了,但没有创建任何过程。
我的问题:
- 我的代码有什么问题?
- 有没有更好/更简单的方法来实现同样的目标?
下面的代码尝试遍历一个值表(外循环),并针对该表中的每一行,遍历另一个具有一些缺失值的表(内循环)。每当在内循环表中发现缺失值时,如果外表中存在可用的值,我会用该值更新内表。
CREATE OR REPLACE PROCEDURE impute()
LANGUAGE plpgsql
AS $$
DECLARE
cntry_dec_means RECORD;
table_row RECORD;
BEGIN
<<outer_loop>>
FOR cntry_dec_means IN
SELECT * FROM country_decade_means
LOOP
-- if all imputed values are null, don't do anything and move to the next row
IF ISNULL(cntry_dec_means.gdppc)
AND ISNULL(cntry_dec_means.gdp)
AND ISNULL(cntry_dec_means.ed_gdp)
AND ISNULL(cntry_dec_means.population) THEN
CONTINUE;
-- get matching rows from the actual table
<<inner_loop>>
FOR table_row IN
SELECT * FROM inls625_impute
WHERE country = cntry_dec_means.country
AND decade = cntry_dec_means.decade
LOOP
IF (table_row.gdppc IS NULL) AND (cntry_dec_means.gdppc IS NOT NULL) THEN
UPDATE inls625_impute SET gdppc = cntry_dec_means.gdppc
WHERE inls625_impute.country = cntry_dec_means.country
AND inls625_impute.decade = cntry_dec_means.decade;
END LOOP; --<<inner_loop>>
END LOOP; --<<outer_loop>>
END;
$$;
更新
也感谢@prasad 阐明了如何使用循环标签。不幸的是,它似乎对我不起作用。
以下是错误(在 psql 中):
CREATE OR REPLACE PROCEDURE impute()
LANGUAGE plpgsql
AS $$"
LINE 1: $$;
^
INLS-623-Labs=# table_row RECORD;
ERROR: syntax error at or near "table_row"
LINE 1: table_row RECORD;
^
INLS-623-Labs=# BEGIN
INLS-623-Labs-# <<outer_loop>>
INLS-623-Labs-# FOR cntry_dec_means IN
INLS-623-Labs-# SELECT * FROM country_decade_means
INLS-623-Labs-# LOOP
INLS-623-Labs-# -- if all imputed values are null, don't do anything and move to the next row
INLS-623-Labs-# IF ISNULL(cntry_dec_means.gdppc)
INLS-623-Labs-# AND ISNULL(cntry_dec_means.gdp)
INLS-623-Labs-# AND ISNULL(cntry_dec_means.ed_gdp)
INLS-623-Labs-# AND ISNULL(cntry_dec_means.population) THEN
INLS-623-Labs-# CONTINUE outer_loop;
ERROR: syntax error at or near "<<"
LINE 2: <<outer_loop>>
^
INLS-623-Labs=#
INLS-623-Labs=# -- get matching rows from the actual table
INLS-623-Labs=# <<inner_loop>>
INLS-623-Labs-# FOR table_row IN
INLS-623-Labs-# SELECT * FROM inls625_impute
INLS-623-Labs-# WHERE country = cntry_dec_means.country
INLS-623-Labs-# AND decade = cntry_dec_means.decade
INLS-623-Labs-# LOOP
INLS-623-Labs-# IF ISNULL(table_row.gdppc) AND NOT ISNULL(cntry_dec_means.gdppc) THEN
INLS-623-Labs-# UPDATE inls625_impute SET gdppc = cntry_dec_means.gdppc
INLS-623-Labs-# WHERE inls625_impute.country = cntry_dec_means.country
INLS-623-Labs-# AND inls625_impute.decade = cntry_dec_means.decade;
ERROR: syntax error at or near "<<"
LINE 1: <<inner_loop>>
^
INLS-623-Labs=# END LOOP inner_loop;
ERROR: syntax error at or near "LOOP"
LINE 1: END LOOP inner_loop;
^
INLS-623-Labs=# END LOOP outer_loop;
ERROR: syntax error at or near "LOOP"
LINE 1: END LOOP outer_loop;
^
INLS-623-Labs=# END;
WARNING: there is no transaction in progress
COMMIT
INLS-623-Labs=# $$;
INLS-623-Labs$#
此外,我复现了@prasad 提供的示例并得到了类似的错误:
CREATE OR REPLACE PROCEDURE t_proc()
LANGUAGE plpgsql
AS $$"
LINE 1: $$;
^
INLS-623-Labs=# t2_rec RECORD;
ERROR: syntax error at or near "t2_rec"
LINE 1: t2_rec RECORD;
^
INLS-623-Labs=# BEGIN
INLS-623-Labs-# <<outer_loop>>
INLS-623-Labs-# FOR t_rec IN
INLS-623-Labs-# SELECT * FROM t
INLS-623-Labs-# LOOP
INLS-623-Labs-# IF t_rec.name IS NULL AND t_rec.data IS NULL THEN
INLS-623-Labs-# CONTINUE outer_loop;
ERROR: syntax error at or near "<<"
LINE 2: <<outer_loop>>
^
INLS-623-Labs=# END IF;
ERROR: syntax error at or near "IF"
LINE 1: END IF;
^
INLS-623-Labs=# RAISE NOTICE 't.id value: %', t_rec.id;
ERROR: syntax error at or near "RAISE"
LINE 1: RAISE NOTICE 't.id value: %', t_rec.id;
^
INLS-623-Labs=# <<inner_loop>>
INLS-623-Labs-# FOR t2_rec IN
INLS-623-Labs-# SELECT * FROM t2 WHERE t2.id = t_rec.id
INLS-623-Labs-# LOOP
INLS-623-Labs-# RAISE NOTICE 't2.id value: %', t2_rec.id;
ERROR: syntax error at or near "<<"
LINE 1: <<inner_loop>>
^
INLS-623-Labs=# END LOOP inner_loop;
ERROR: syntax error at or near "LOOP"
LINE 1: END LOOP inner_loop;
^
INLS-623-Labs=# END LOOP outer_loop;
ERROR: syntax error at or near "LOOP"
LINE 1: END LOOP outer_loop;
^
INLS-623-Labs=# END;
WARNING: there is no transaction in progress
COMMIT
INLS-623-Labs=# $$;
从技术上来说,仅
END IF;
失踪了两次。是的。简单的
UPDATE
声明。它不仅更简单、更快速,而且是正确的。您的程序(即使使用固定语法)也容易受到竞争条件的影响。在读取数据和最终到达之间
UPDATE
,并发事务可能会干扰。您可以使用显式锁定来防止这种情况。但不要这样做。请特别注意这个表述:
如果所有这些列都为空,则您只想跳过更新。(另外,
c.gdppc
在任何情况下都不能为空。)参见:以下是如何编写程序代码。我使用两个表格
t
来t2
展示您在代码中尝试的语法(LOOP、IF-END IF 等)的正确方法。我正在使用 PostgreSQL 版本 14,并且命令从
psql
shell 运行。表格
t
有三列,上面的空值是null
值。表格t2
只有一列。程序:
运行该过程并检查输出:
更多参考: https://www.postgresql.org/docs/14/plpgsql-control-structures.html