检查plpgsql中是不是存在变量的最佳实践?
Posted
技术标签:
【中文标题】检查plpgsql中是不是存在变量的最佳实践?【英文标题】:Best practice to check if a variable exists in plpgsql?检查plpgsql中是否存在变量的最佳实践? 【发布时间】:2012-10-21 17:32:07 【问题描述】:例如,我有一个存储过程来从 csv 文件导入数据并将读取的数据写入 SQL 表。 我有一个定义如下的表:
CREATE TABLE person (id int, name text, age int, married boolean);
首先我检查记录是否已经存在,如果存在我更新,如果不存在 - 插入。 每个记录字段可能有不同的类型,因此将 SQL 命令的结果分配给标量变量列表:
SELECT name, age, married INTO v_name, v_age, v_married [..]
假设每一列都被声明为可选的(允许为 NULL)。那么检查哪个变量(v_name、v_age、v_married)不为 NULL 并且可以处理的最佳方法是什么?
我找到了很多解决方案:
如果没有找到则 当没有找到数据时 如果 v_age 不为空,则 [...]当我必须检查多个列 (col) 时,我现在正在使用上面提到的最后一种方式使用动态解决方案:
list_of_columns := ARRAY['name','age','married'];
FOREACH x IN ARRAY list_of_columns LOOP
EXECUTE 'SELECT ' || x
|| ' FROM person
WHERE id = ' || quote_literal(v_id)
INTO y;
IF x = 'name' AND (y != v_name OR y IS NULL) THEN
UPDATE person
SET name = v_name
WHERE id = v_id;
ELSIF x = 'age' AND (y != v_age OR y IS NULL) THEN
UPDATE person
SET age = v_age
WHERE id = v_id;
ELSIF x = 'married' AND (y != v_married OR y IS NULL) THEN
UPDATE person
SET married= v_married
WHERE id = v_id;
END IF;
END LOOP;
我正在寻找考虑到最佳实践和性能的最佳解决方案。 任何帮助表示赞赏!
【问题讨论】:
你不必那么苛刻,如果你看不懂写的是什么(我不怪你)我会改进这个问题。对我来说,我自己的代码是完全可以理解的,有时很难判断,或者对其他人来说也很清楚。 我已经简化并更改了列名,希望现在更好。 现在好多了。person = v_person
应该是 maried = v_married
?
呵呵,抱歉有点晚了;) 没错。
只是一个注释 - 动态 SQL 对“FOUND”变量没有影响,也不会引发异常 DATA_NOT_FOUND。如果你需要它,你应该使用 GET DIAGNOSTICS 语句
【参考方案1】:
我认为,您可以从根本上按照以下方式改进整个过程:
BEGIN;
CREATE TEMP TABLE tmp_p ON COMMIT DROP AS
SELECT * FROM person LIMIT 0;
COPY tmp_p FROM '/absolute/path/to/file' FORMAT csv;
UPDATE person p
SET name = t.name
,age = t.age
,married = t.person
FROM tmp_p t
WHERE p.id = t.id
AND (p.name IS DISTINCT FROM t.name OR
p.age IS DISTINCT FROM t.age OR
p.married IS DISTINCT FROM t.married);
INSERT INTO person p(id, name, age, married, ...)
SELECT id, name, age, married, ...
FROM tmp_p t
WHERE NOT EXISTS (SELECT 1 FROM person x WHERE x.id = t.id);
COMMIT; -- drops temp table because of ON COMMIT DROP
解释
COPY
将您的 CSV 文件复制到具有匹配布局的临时表中。我用CREATE TABLE AS ... LIMIT 0
复制了目标表的布局,你可能需要适应...
UPDATE
现有行。避免在 WHERE
子句的最后 3 行进行空更新(不会有任何变化)。如果您想在 UPDATE 中跳过 NULL 值(真的吗?),请使用 (可能有用,但实际上不在您的代码中。)COALESCE(t.name, p.name)
之类的表达式。在 NULL
的情况下,这会退回到现有值。
INSERT
不存在的行。为此使用NOT EXISTS
半联接。
所有在一个事务中,这样您就不会在过程中出现问题时得到半生不熟的结果。临时表在事务结束时被删除,因为我是这样创建的。
【讨论】:
@Borys:请注意我删除COALESCE
表达式的更新 - 不适合您问题中的代码,这样更有意义。
谢谢,但是即使不需要(记录不存在),每次都执行更新/插入/创建临时表不会产生性能开销吗?
@Borys:要明确一点:这个 SQL 脚本是按表一次运行的,而不是你似乎暗示的按行运行。应该更快地执行更多,因为它总共只需要两个 SQL 命令,并且只操作需要操作的行并且只操作一次。您的原始代码执行 1 +(列数)命令每行仅用于更新 - 可能会多次更新同一行。此外,基于集合的操作通常比逐行迭代要快得多。运行测试,差异应该很明显。
感谢您的回答。尽管在这种特殊情况下我不能使用这种方法从文件中导入数据(因为文件非常小和验证问题),但它对其他文件会非常有帮助。尤其是这个 UPDATE - 在这个构造之前没有见过。
@Borys:我的回答只是普通 SQL。如果将其包装到 plpgsql 函数中,则必须松开 COMMIT
,因为每个函数都是自动事务,BEGIN
具有不同的含义。 Details about stored procedures / plpgsql functions in this related answer on dba.SE.以上是关于检查plpgsql中是不是存在变量的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章
Java SafeVarargs 注释,是不是存在标准或最佳实践?