检查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 注释,是不是存在标准或最佳实践?

检查 C 中是不是存在文件的最佳方法是啥?

检查 C 中是不是存在文件的最佳方法是啥?

检查sql server ce中是不是存在项目的最佳方法?

使用 React Context 中的使用效果检查本地存储中是不是已经存在令牌的最佳方法

检查 DataReader 中是不是存在列的最佳方法