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

时间:2015-04-09 17:40:21

标签: sql postgresql sql-update hstore postgresql-9.4

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

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 |
+----+----+------+----+

使用foo中的值更新bar的“最佳”方法是什么?

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

我正在使用PostgreSQL 9.4。

2 个答案:

答案 0 :(得分:3)

如果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):
  

hstore是否包含任何指定的密钥?

如果情况并非如此,那么最便宜的是不要触摸该行。
否则,列中至少有一列(但不一定是全部!)会收到UPDATECOALESCE进来的地方。

然而,per documentation:

  

值(但不是键)可以是SQL NULL

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

  • 找不到钥匙' f2'`
  • b.row_data->'f2'返回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检查单个密钥:

  

hstore是否包含密钥?

答案 1 :(得分:0)

所以你经过一次简单的更新?由于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;