从另一个 table 中的 hstore 列更新现有行

UPDATE existing rows from hstore column in another table

我需要编写一个查询(或函数),用存储在另一个 table 的 hstore 列中的值更新 table 中的现有记录。例如:

create temp table foo(id int primary key, f1 int, f2 text, f3 int);
insert into foo values
  (1, 1, 'jack', 1),
  (2, 2, 'ted' , 2),
  (3, 3, 'fred', 3);

create temp table bar(foo_id int references foo(id), row_data hstore);
insert into bar values
  (1, 'f1=>0, f2=>bill'::hstore),
  (2, 'f1=>0, f2=>will, f3=>0'::hstore),
  (3, 'f3=>0'::hstore);

只有在 hstore 列中有值的列才应该更新,因此处理后,所需的结果将是:

select * from foo;
+----+----+------+----+
| id | f1 |  f2  | f3 |
+----+----+------+----+
|  1 |  0 | bill |  1 |
|  2 |  0 | will |  0 |
|  3 |  3 | fred |  0 |
+----+----+------+----+

bar 中的值更新 foo 的 "best" 方法是什么?

注意:我将 最佳 定义为最容易编写代码。虽然性能始终很重要,但这是一个批处理作业,速度并不像用户等待结果时那样重要。

我正在使用 PostgreSQL 9.4。

所以您是在进行简单的更新?由于 f1f3 是整数,您需要转换它们。否则它只是:

UPDATE foo SET f1 = (row_data->'f1')::integer,
    f2 = row_data->'f2',
    f3 = (row_data->'f3')::integer
    FROM bar WHERE foo.id = foo_id;

如果 hstore 列中未提供任何内容,则保留原始列值 ...

使用 COALESCE

的简单方法
UPDATE foo f
SET    f1 = COALESCE((b.row_data->'f1')::int, f1)
     , f2 = COALESCE( b.row_data->'f2'      , f2)
     , f3 = COALESCE((b.row_data->'f3')::int, f3)
FROM   bar b
WHERE  f.id = b.foo_id
AND    b.row_data ?| '{f1,f2,f3}'::text[];
  • 添加的最后一行立即从 UPDATE 中排除未受影响的行:?| 运算符检查 (per documentation):

does hstore contain any of the specified keys?

如果不是这种情况,则根本不触摸该行是最便宜的。
否则,至少有一个(但不一定是所有!)列收到 UPDATE。这就是 COALESCE 的用武之地。

然而,per documentation:

A value (but not a key) can be an SQL NULL.

所以COALESCE无法区分这里NULL的两种可能含义:

  • 找不到密钥 'f2'`。
  • b.row_data->'f2' returns NULL 作为 f2.
  • 的新值

也适用于 NULL 值

UPDATE foo f
SET    f1 = CASE WHEN b.row_data ? 'f1'
                 THEN (b.row_data->'f1')::int ELSE f1 END
     , f2 = CASE WHEN b.row_data ? 'f2'
                 THEN b.row_data->'f2'        ELSE f2 END
     , f3 = CASE WHEN b.row_data ? 'f3'
                 THEN (b.row_data->'f3')::int ELSE f3 END
FROM   bar b
WHERE  f.id = b.foo_id
AND    b.row_data ?| '{f1,f2,f3}'::text[];

? operator 检查单个键:

does hstore contain key?