例如,我有一个存储过程从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并且可以处理的最佳方法是什么?
我找到了很多解决方案:
或动态解决方案我现在正在使用我上面提到的最后一种方式,当我必须检查多个列(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;
我正在寻找考虑到最佳实践和性能的最佳解决方案。 任何帮助表示赞赏!
答案 0 :(得分: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
半联接。
所有在一个交易中,所以如果在整个过程中出现问题,您不会得到半生不好的结果。临时表在事务结束时被删除,因为我是这样创建的。