如何在 JSON 键的 postgres 更新语句中增加值

Posted

技术标签:

【中文标题】如何在 JSON 键的 postgres 更新语句中增加值【英文标题】:How to increment value in postgres update statement on JSON key 【发布时间】:2014-11-15 11:11:27 【问题描述】:

更新关系表时:

CREATE TABLE foo ( id serial primary key, credit numeric);
UPDATE foo SET bar = bar + $1 WHERE id = $2;

但是 JSON 中的等价物不起作用:

CREATE TABLE foo ( id serial primary key, data json);
UPDATE foo SET data->'bar' = data->'bar' + $1 WHERE id = $2;

我得到的错误是error: syntax error at or near "->" - 这是相当模糊的。

我该怎么做?

我正在使用 postgres 9.3.4


鉴于@GordonLinoff 在下面的评论,我创建了一个功能请求:https://postgresql.uservoice.com/forums/21853-general/suggestions/6466818-create-update-delete-on-json-keys

如果你也喜欢这个功能,你可以投票。

【问题讨论】:

我不认为你可以用update 做你想做的事。看到这个问题:***.com/questions/18209625/…. @GordonLinoff 嗯 - 看起来像 CRUD,postgres 只支持对 JSON 类型属性的读取操作?这似乎是 postgres 对 JSON 支持的一个相当大的差距;有没有计划增加对此的支持,还是我们应该坚持使用 postgres 作为关系数据库(需要创建/更新/删除)? 我们是否应该坚持使用 postgres 作为关系数据库? PostgreSQL 一个关系型数据库。请注意,JSON 旨在作为一种传输格式,而 PostgreSQL 9.3 存储的是 JSON(文本)而不是对象。它只会是一个评估过的对象。我认为 PostgreSQL 在读取属性方面已经走得很远,它是一种关系 DBMS,而不是像 javascript 或 Python 这样的过程语言。 9.4 扩展了对 JSON 的支持,并且考虑到一些(IMO 被误导)用户希望 JSON 取代规范化和良好设计的 JSON 需求,我认为它可能会走得更远。 【参考方案1】:

根据@joonas.fi 和pozs's 的回答,我想出了一个更“漂亮”的解决方案

UPDATE foo 
SET data = jsonb_set(data, 'bar', (COALESCE(data->>'bar','0')::int + 1)::text::jsonb)
WHERE id = 1;

【讨论】:

【参考方案2】:

您可以使用 jsonb 执行此操作,至少使用 Postgres 9.5.2。

给定下表:

CREATE TABLE users (id INT, counters JSONB NOT NULL DEFAULT '');

有样本数据:

INSERT INTO users (id, counters) VALUES (1, '"bar": 0');

SELECT * FROM users;

 id |    counters
----+------------
  1 | "bar": 0

您可以在 JSON 中自动增加“bar”键:

UPDATE users SET counters = counters || CONCAT('"bar":', COALESCE(counters->>'bar','0')::int + 1, '')::jsonb WHERE id = 1;

SELECT * FROM users;

 id |    counters
----+------------
  1 | "bar": 1

它并不漂亮,但它有效。

这里按步骤分解:

您可以通过OR'ing jsonb 对象将jsonb 中的键设置为显式值:

UPDATE users SET counters = counters || '"bar": 314'::jsonb WHERE id = 1;

SELECT * FROM users;

 id |     counters
----+--------------
  1 | "bar": 314

现在剩下要做的就是在 CONCAT() 的帮助下动态构建字符串,同时演示递增(27)一个未定义的键(在 COALESCE() 的帮助下默认初始值):

UPDATE users SET counters = counters || CONCAT('"foo":', COALESCE(counters->>'foo','0')::int + 27, '')::jsonb WHERE id = 1;

SELECT * FROM users;

 id |          counters
----+-------------------------
  1 | "bar": 314, "foo": 27

鲍勃是你的叔叔。 :)

【讨论】:

【参考方案3】:

嵌套 JSONB 数据:

发件人:

table_name.data_col = "a": "b": "c": 1 // JSONB

收件人:

table_name.data_col = "a": "b": "c": 2 // JSONB

使用这个:

UPDATE 
  table_name 
SET 
  data_col = jsonb_set(
    data_col, 
    'a,b,c', 
    (
      COALESCE(
        data_col#>'a,b,c', '0'
      ):: int + 1
    ):: text :: jsonb
  ) 
WHERE 
  id = '<id>';

table_name 是您的表名,data_col 是您的 JSONB

注意事项:

如果不明显你也可以decrement 使用- 1 等其他 操作。 用于 json 操作的 PostgreSQL docs 非常有用

【讨论】:

以上是关于如何在 JSON 键的 postgres 更新语句中增加值的主要内容,如果未能解决你的问题,请参考以下文章

如何将 postgres 数据库转换为 JSON 文件?

如何将参数从命令行传递到 python 脚本以读取和更新 json 文件中存在的某些键的值

在 Postgres JSON 数组中查询

如何修改postgres数据库中的json字段

如何仅从 postgres 获取特定键的 jsonb?

如何在 JSON 对象中获取键的值 [重复]