Kiểu dữ liệu của cột people
là json
kế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 BY
trên nó. Hơn:
jsonb
có 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ạn là jsonb
sử 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 text
thay 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 text
kết quả trong kết quả để bắt đầu.
GROUP BY
trên text
dữ 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 LATERAL
IMO, 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ể. name
là 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) name
chỉ đí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 name
giải quyết thành một cột duy nhất value
khi được sử dụng trong SELECT
danh 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.name
nê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 people
có 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: