如何从PostgreSQL的JSON []数组中删除已知元素?


8

我面临有关在PostgreSQL中使用JSON数据类型的问题。我试图实现在数据库中存储非规范化的Java模型。该模型具有复杂对象列表。因此,我决定在本地PostgreSQL数组中将其建模为JSON。

这是我的表创建语句的摘要:

CREATE TABLE test.persons
(
  id UUID,
  firstName TEXT,
  lastName TEXT,
  communicationData JSON[],
  CONSTRAINT pk_person PRIMARY KEY (id)
);

如您所见,这是一个具有JSON中通讯数据对象列表的人。这些对象之一可能看起来像这样:

{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf6"}

我可以使用PostgreSQL的array_append轻松地将这样的JSON对象附加到数组。但是,我无法从数组中删除已知值。考虑一下这个SQL语句:

UPDATE test.persons
SET communicationData = array_remove(
      communicationData, 
      '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf6"}'::JSON
    )
WHERE id = 'f671eb6a-d603-11e3-bf6f-07ba007d953d';

失败ERROR: could not identify an equality operator for type json。您是否有提示我如何从JSON数组中删除已知值?我也知道也可以按位置删除数组中的一个...

PostgreSQL版本是9.3.4。

Answers:


11

jsonb 在Postgres 9.4或更高版本中

考虑Postgres 9.4中jsonb数据类型 -“ b”代表“ binary”。除此之外,还有一个相等运算符。大多数人会想要切换。=jsonb

关于jsonb的Depesz博客。

json

没有=为数据类型定义运算符json,因为没有为整个json值建立相等性的明确定义的方法。但请参见下文。

可以强制转换为text,然后使用=运算符。这很短,但是仅当您的文本表示形式匹配时才有效。本质上不可靠,除了极端情况。看到:

或者,您可以unnest使用数组并使用->>运算符来.. get JSON object field as text并比较各个字段。

测试台

2行:第一个类似问题,第二个具有简单值。

CREATE TABLE tbl (
   tbl_id int PRIMARY KEY
 , jar    json[]
);

INSERT INTO t VALUES
   (1, '{"{\"value\" : \"03334/254146\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f5\"}"
        ,"{\"value\" : \"03334/254147\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f6\"}"
        ,"{\"value\" : \"03334/254148\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f7\"}"}')

 , (2, '{"{\"value\" : \"a\", \"typeId\" : \"x\"}"
        ,"{\"value\" : \"b\", \"typeId\" : \"y\"}"
        ,"{\"value\" : \"c\", \"typeId\" : \"z\"}"}');

演示版

演示1:你可以使用array_remove()text表示(不可靠)。

SELECT tbl_id
     , jar, array_length(jar, 1) AS jar_len
     , jar::text[] AS t, array_length(jar::text[], 1) AS t_len
     , array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text) AS t_result
     , array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text)::json[] AS j_result
FROM   tbl;

演示2:嵌套单个元素的数组和测试字段。

SELECT tbl_id, array_agg(j) AS j_new
FROM   tbl, unnest(jar) AS j   -- LATERAL JOIN
WHERE  j->>'value' <> '03334/254146'
AND    j->>'typeId' <> 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5'
GROUP  BY 1;

演示3:行类型的替代测试。

SELECT tbl_id, array_agg(j) AS j_new
FROM   tbl, unnest(jar) AS j   -- LATERAL JOIN
WHERE  (j->>'value', j->>'typeId') NOT IN (
         ('03334/254146', 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5')
        ,('a', 'x')
       )
GROUP  BY 1;

UPDATE 按照要求

最后,这就是实现您的方法UPDATE

UPDATE tbl t
SET    jar = j.jar
FROM   tbl t1
CROSS  JOIN LATERAL (
   SELECT ARRAY(
      SELECT j
      FROM   unnest(t1.jar) AS j  -- LATERAL JOIN
      WHERE  j->>'value'  <> 'a'
      AND    j->>'typeId' <> 'x'
      ) AS jar
   ) j
WHERE  t1.tbl_id = 2              -- only relevant rows
AND    t1.tbl_id = t.tbl_id;

db <> 在这里拨弄

关于隐式LATERAL JOIN

关于取消嵌套数组:

数据库设计

为了简化您的情况,请考虑一个规范化的架构json值的单独表(而不是数组列),以与主表的关系为1 :。


它像一种魅力。是的,使用标准化数据会更容易,但我处于98%的读取,2%的写入情况。因此,我想尝试非规范化:-)有没有为Postgres 9.4计划的版本,这可能会对原始问题有所帮助?
spa

@spa:实际上,Postgres 9.4将带来jsonb。我希望你会喜欢它。添加了带有链接的章节。
Erwin Brandstetter,2014年

这太酷了。感谢您的注意。
spa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.