0

例如,我有一个存储过程来从 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并且可以处理的最好方法是什么?

我找到了很多解决方案:

  • 如果没有找到
  • 当 NO_DATA_FOUND 那么
  • 如果 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;

我正在寻找考虑到最佳实践和性能的最佳解决方案。任何帮助表示赞赏!

4

1 回答 1

4

我认为,您可以从根本上改进整个过程:

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半连接。

  • All in one transaction so you don't end up with a half-baked result in the case of a problem along the way. The temp table is dropped at the end of the transaction because I created it that way.

于 2012-10-21T23:52:52.653 回答