Làm cách nào để xóa các phần tử đã biết khỏi mảng JSON [] trong PostgreSQL?


8

Tôi đang gặp vấn đề liên quan đến việc sử dụng kiểu dữ liệu JSON trong PostgreSQL. Tôi cố gắng đạt được việc lưu trữ một mô hình Java được chuẩn hóa trong DB. Các mô hình tính năng danh sách các đối tượng phức tạp. Vì vậy, tôi quyết định mô hình hóa chúng dưới dạng JSON trong mảng PostgreSQL gốc.

Đây là đoạn trích trong tuyên bố tạo bảng của tôi:

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

Như bạn có thể thấy đó là một người có danh sách các đối tượng dữ liệu truyền thông trong JSON. Một trong những đối tượng như vậy có thể trông như thế này:

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

Tôi có thể dễ dàng nối thêm một đối tượng JSON như vậy vào một mảng bằng mảng_append của PostgreSQL. Tuy nhiên, tôi thất bại trong việc loại bỏ một giá trị đã biết khỏi mảng. Hãy xem xét câu lệnh SQL này:

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';

Điều này thất bại với ERROR: could not identify an equality operator for type json. Bạn có gợi ý làm thế nào tôi có thể xóa một giá trị đã biết khỏi mảng JSON không? Cũng có thể xóa theo vị trí trong mảng, vì tôi biết rằng người ta cũng ...

Phiên bản PostgreSQL là 9.3.4.

Câu trả lời:


11

jsonb trong Postgres 9,4 trở lên

Xem xét jsonbkiểu dữ liệu trong Postgres 9,4 - 'b' cho 'nhị phân'. Trong số những thứ khác, có một toán tử bình đẳng =chojsonb . Hầu hết mọi người sẽ muốn chuyển đổi.

Blog của Depesz về jsonb.

json

Không có =toán tử được xác định cho kiểu dữ liệu json, vì không có phương thức được xác định rõ để thiết lập đẳng thức cho toàn bộ jsongiá trị. Nhưng xem bên dưới.

Bạn có thể cast vào textvà sau đó sử dụng các =nhà điều hành. Điều này là ngắn, nhưng chỉ hoạt động nếu đại diện văn bản của bạn xảy ra để phù hợp. Không đáng tin cậy, ngoại trừ các trường hợp góc. Xem:

Hoặc bạn có thể unnestmảng và sử dụng ->>toán tử để .. get JSON object field as textvà so sánh các trường riêng lẻ.

Bàn kiểm tra

2 hàng: cái đầu tiên giống như trong câu hỏi, cái thứ hai có giá trị đơn giản.

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\"}"}');

Trình diễn

Bản demo 1: bạn có thể sử dụng array_remove()với các textđại diện (không đáng tin cậy).

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;

Bản demo 2: không nhất định các mảng và các trường kiểm tra của các phần tử riêng lẻ.

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;

Demo 3: thử nghiệm thay thế với loại hàng.

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 như yêu cầu

Cuối cùng, đây là cách bạn có thể thực hiện 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 <> fiddle ở đây

Về ẩn LATERAL JOIN:

Về mảng không kiểm soát:

Thiết kế DB

Để đơn giản hóa tình huống của bạn, hãy xem xét một lược đồ chuẩn hóa : một bảng riêng cho các jsongiá trị (thay vì cột mảng), đã tham gia vào mối quan hệ: 1 với bảng chính.


Nó hoạt động như một say mê. Có, sẽ dễ dàng hơn với dữ liệu được chuẩn hóa, nhưng tôi đang ở trong kịch bản ghi 98%, 2% ghi. Vì vậy, tôi muốn thử nghiệm với việc không chuẩn hóa :-) Có bất cứ điều gì được lên kế hoạch cho Postgres 9.4 có thể giúp ích cho câu hỏi ban đầu không?
spa

@spa: Thật ra, Postgres 9.4 sẽ mang lại jsonb. Tôi hy vọng bạn sẽ thích nó. Đã thêm một chương với các liên kết.
Erwin Brandstetter

Điều đó thật tuyệt. Cảm ơn cho những người đứng đầu lên.
spa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.