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

spa*_*spa 8 postgresql update array json

我遇到了在 PostgreSQL 中使用 JSON 数据类型的问题。我尝试实现在数据库中存储非规范化的 Java 模型。该模型具有复杂对象列表。因此,我决定将它们建模为原生 PostgreSQL 数组中的 JSON。

这是我的表创建语句的精简片段:

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

如您所见,它是一个以 JSON 格式显示通信数据对象列表的人。其中一个对象可能如下所示:

{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf6"}
Run Code Online (Sandbox Code Playgroud)

我可以使用 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';
Run Code Online (Sandbox Code Playgroud)

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

PostgreSQL 版本是 9.3.4。

Erw*_*ter 11

jsonb 在 Postgres 9.4 或更高版本中

考虑Postgres 9.4 或更高版本中的jsonb数据类型。末尾的“b”代表“二进制”。除其他外,还有一个相等运算符 ( =) forjsonb。大多数人会想要转换。

关于 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\"}"}');
Run Code Online (Sandbox Code Playgroud)

演示

演示 1

可以使用array_remove()withtext表示(不可靠)。

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;
Run Code Online (Sandbox Code Playgroud) 演示 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;
Run Code Online (Sandbox Code Playgroud) 演示 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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

db<>在这里摆弄

关于隐式LATERAL JOIN

关于取消嵌套数组:

数据库设计

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