Làm thế nào để unest và GROUP BY các phần tử của một mảng JSON?


8

Cho bandbảng, với một jsoncột giữ một mảng:

id | people
---+-------------
1  | ['John', 'Thomas']
2  | ['John', 'James']
3  | ['James', 'George']

Làm thế nào để liệt kê số lượng ban nhạc mỗi tên là một phần của?
Sản phẩm chất lượng:

name   | count
-------+------------
John   | 2
James  | 2
Thomas | 1
George | 1

Câu trả lời:


6

Kiểu dữ liệu của cột peoplejsonkết quả của json_array_elements(people). Và không có toán tử đẳng thức ( =) cho kiểu dữ liệu json. Vì vậy, bạn cũng không thể chạy GROUP BYtrên nó. Hơn:

jsonbcó một toán tử đẳng thức, vì vậy "cách giải quyết" trong câu trả lời của bạnjsonbsử dụng và sử dụng tương đương jsonb_array_elements(). Các diễn viên thêm chi phí:

jsonb_array_elements(people::jsonb)

Kể từ Postgres 9.4, chúng tôi cũng có json_array_elements_text(json)các phần tử mảng trả về text. Liên quan:

Vì thế:

SELECT p.name, count(*) AS c
FROM   band b, json_array_elements_text(b.people) p(name)
GROUP  BY p.name;

Có vẻ thuận tiện hơn khi lấy tên textthay vì các jsonbđối tượng (trích dẫn kép trong biểu diễn văn bản) và "đầu ra mong muốn" của bạn cho biết bạn muốn / cần textkết quả trong kết quả để bắt đầu.

GROUP BYtrên textdữ liệu cũng rẻ hơn so với trên jsonb, vì vậy "cách giải quyết" thay thế này phải nhanh hơn vì hai lý do. (Kiểm tra với EXPLAIN (ANALYZE, TIMING OFF).)

Đối với hồ sơ, không có gì sai với câu trả lời ban đầu của bạn . Dấu phẩy ( ,) chỉ là "chính xác" như CROSS JOIN LATERAL. Đã được định nghĩa trước đó trong SQL tiêu chuẩn không làm cho nó kém hơn. Xem:

Nó cũng không dễ di chuyển hơn đối với các RDBMS khác và vì jsonb_array_elements()hoặc json_array_elements_text()không thể di chuyển sang các RDBMS khác để bắt đầu, điều đó cũng không liên quan. Truy vấn ngắn không rõ ràng hơn với CROSS JOIN LATERALIMO, nhưng bit cuối cùng chỉ là ý kiến ​​cá nhân của tôi.

Tôi đã sử dụng bí danh bảng và cột rõ ràng hơn và p(name)tham chiếu đủ điều kiện bảng p.nameđể bảo vệ chống lại các tên trùng lặp có thể. namelà một từ phổ biến như vậy, nó cũng có thể bật lên dưới dạng tên cột trong bảng bên dưới band, trong trường hợp đó, nó sẽ âm thầm giải quyết band.name. Biểu mẫu đơn giản json_array_elements_text(people) namechỉ đính kèm một bí danh bảng , tên cột vẫn value, như được trả về từ hàm. Nhưng namegiải quyết thành một cột duy nhất valuekhi được sử dụng trong SELECTdanh sách. Nó xảy ra để làm việc như mong đợi . Nhưng một tên cột thực sự name(nếu band.namenên tồn tại) sẽ liên kết đầu tiên. Trong khi điều đó sẽ không cắn trong ví dụ đã cho, nó có thể là một khẩu súng được nạp đạn trong các trường hợp khác.

Đừng sử dụng "tên" chung làm định danh để bắt đầu. Có lẽ đó chỉ là cho trường hợp thử nghiệm đơn giản.


Nếu cột peoplecó thể chứa bất cứ thứ gì ngoại trừ một mảng JSON đơn giản , một trong hai truy vấn sẽ kích hoạt một ngoại lệ. Nếu bạn không thể đảm bảo tính toàn vẹn dữ liệu, bạn có thể muốn bảo vệ bằng json_typeof():

SELECT p.name, count(*) AS c
FROM   band b, json_array_elements_text(b.people) p(name)
WHERE  json_typeof(b.people) = 'array'
GROUP  BY 1; -- optional short syntax since you seem to prefer short syntax

Không bao gồm các hàng vi phạm từ truy vấn.

Liên quan:


4

Dựa trên nhận xét @ ypercubeᵀᴹ tôi đã kết thúc với:

SELECT name, count(*) as c
FROM band 
CROSS JOIN LATERAL jsonb_array_elements(people::jsonb) as name
GROUP BY name;

Chỉ sử dụng jsonb_array_elementsthay thế unnest.


-1

Dành cho ai đó trong MySQL

SELECT
  JSON_EXTRACT(people, CONCAT('$[', idx, ']')) AS name, count(*) as count
FROM yourtable
JOIN subtable AS indexes
WHERE JSON_EXTRACT(people, CONCAT('$[', idx, '].id')) IS NOT NULL
group by name

với phụ đề như: Colum: idx, hàng: 0,1,2,3,4,5,6,7,8,9 ...

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.