Chọn các cột bên trong json_agg


21

Tôi có một truy vấn như:

SELECT a.id, a.name, json_agg(b.*) as "item"
  FROM a
  JOIN b ON b.item_id = a.id
 GROUP BY a.id, a.name;

Làm cách nào tôi có thể chọn các cột bđể tôi không có b.item_idtrong đối tượng JSON?

Tôi đã đọc về ROWnó, nhưng nó trả về một đối tượng JSON như:

{"f1": "Foo", "f2": "Bar"}

Tôi sẽ cần phải ánh xạ lại đối tượng JSON sau khi nó được tìm nạp để khớp với các khóa cột thích hợp. Tôi muốn tránh điều đó và giữ tên cột ban đầu.

Câu trả lời:


50

Thật không may, cú pháp SQL không có quy định nào để nói "tất cả các cột ngoại trừ một cột này" . Bạn có thể đạt được mục tiêu của mình bằng cách đánh vần danh sách các cột còn lại trong biểu thức loại hàng :

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Đó là viết tắt của hình thức rõ ràng hơn : . ROW(b.col1, b.col2, b.col3)

Tuy nhiên, tên cột không được bảo tồn trong các biểu thức kiểu hàng. Bạn nhận được các tên khóa chung trong đối tượng JSON theo cách này. Tôi thấy 3 tùy chọn để giữ nguyên tên cột ban đầu:

1. Chuyển sang loại đã đăng ký

Truyền đến một loại hàng nổi tiếng (đã đăng ký). Một loại được đăng ký cho mỗi bảng hoặc dạng xem hiện có hoặc với một CREATE TYPEtuyên bố rõ ràng . Bạn có thể sử dụng bảng tạm thời cho giải pháp đặc biệt (tồn tại trong suốt thời gian của phiên):

CREATE TEMP TABLE x (col1 int, col2 text, col3 date);  -- use adequate data types!

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)::x) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

2. Sử dụng một mục phụ

Sử dụng một subselect để xây dựng một bảng dẫn xuất và tham chiếu toàn bộ bảng . Điều này cũng mang tên cột. Nó dài dòng hơn, nhưng bạn không cần loại đã đăng ký:

SELECT a.id, a.name
     , json_agg((SELECT x FROM (SELECT b.col1, b.col2, b.col3) AS x)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

3. json_build_object()trong Postgres 9.4 trở lên

SELECT a.id, a.name
     , json_agg(json_build_object('col1', b.col1, 'col2', b.col2, 'col3', b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

Liên quan:

Tương tự jsonbvới các chức năng tương ứng jsonb_agg()jsonb_build_object().

Đối với Postgres 9,5 hay muộn cũng thấy câu trả lời a_horse của với một biến thể cú pháp ngắn mới: Postgres thêm toán tử trừ -chojsonb nói "tất cả các phím trừ một phím này" .
Postgres 10 "ngoại trừ một số khóa" được triển khai với cùng một toán tử lấy text[]toán hạng 2 - giống như mlt đã nhận xét.


1
> hoặc một số phím Lưu ý rằng json (b) -text [] khả dụng bắt đầu từ 10.
mlt

Giải pháp 3 làm việc cho tôi như một cơ duyên!
Luiz Fernando da Silva

17

Bắt đầu với 9.6, bạn chỉ cần sử dụng -để xóa khóa khỏi JSONB:

SELECT a.id, a.name, jsonb_agg(to_jsonb(b) - 'item_id') as "item"
FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

to_jsonb(b)sẽ chuyển đổi toàn bộ hàng và - 'item_id'sau đó sẽ xóa khóa với tên item_idkết quả của nó sau đó được tổng hợp.


Các tính năng mới này dường như là những gì OP đã hy vọng. Tôi đã thêm một liên kết đến câu trả lời của tôi.
Erwin Brandstetter

Khi tôi thử biến thể subselect, tôi đã gặp một lỗi liên quan đến json_aggchức năng:function json_agg(record) does not exist
fraxture

@fraxture: sau đó bạn không sử dụng Postgres 9.6
a_horse_with_no_name

Quả thực đó là vấn đề. Có cách nào để lọc các cột trong v9.2 không?
fraxture

8

Bạn thực sự có thể làm điều đó mà không cần nhóm bằng cách sử dụng các truy vấn con

SELECT 
  a.id, a.name, 
  ( 
    SELECT json_agg(item)
    FROM (
      SELECT b.c1 AS x, b.c2 AS y 
      FROM b WHERE b.item_id = a.id
    ) item
  ) AS items
FROM a;

trả lại

{
  id: 1,
  name: "thing one",
  items:[
    { x: "child1", y: "child1 col2"},
    { x: "child2", y: "child2 col2"}
  ]
}

Bài viết này của John Atten thực sự thú vị và có nhiều chi tiết hơn


2

Tôi đã thấy rằng tốt nhất là tạo JSON, sau đó tổng hợp nó. ví dụ

with base as (
select a, b, ('{"ecks":"' || to_json(x) || '","wai":"' || to_json(y) || '","zee":"' || to_json(z) || '"}"')::json c
) select (a, b, array_to_json(array_agg(c)) as c)

Lưu ý điều này có thể được thực hiện dưới dạng truy vấn phụ nếu bạn không thích CTE (hoặc gặp vấn đề về hiệu suất vì sử dụng nó).

Cũng lưu ý, nếu bạn sẽ làm điều này rất nhiều, có thể có ích khi tạo một hàm để bọc các cặp khóa-giá trị cho bạn để mã trông sạch hơn. Bạn sẽ vượt qua chức năng của mình (ví dụ) 'ecks', 'x'và nó sẽ trở lại "ecks": "x".


1

Mặc dù vẫn không có cách nào để làm bất cứ điều gì về việc chọn tất cả các cột trừ một bit, nhưng bạn có thể sử dụng json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))để có được một mảng jsons mỗi định dạng {"col_1":"col_1 value", ...}.

Vì vậy, truy vấn sẽ trông giống như:

SELECT a.id, a.name, json_agg(to_json(b.col_1,b.col_2,b.col_3...)) as item
  FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

và sẽ trả về các hàng như:

id, name, item
8, the_name, [{"col_1":"value_1","col_2":"value_2","col_3":"value_3"...}, {"col_1":"value_1.2","col_2":"value_2.2","col_3":"value_3.2"...},...]
9, the_next_name, [{"col_1":"value_1.3","col_2":"value_2.3","col_3":"value_3.3"...},   {"col_1":"value_1.4","col_2":"value_2.4","col_3":"value_3.4"...},...]
...

(Tôi hiện đang sử dụng Postgres 9.5.3 và không chắc chắn 100% khi hỗ trợ này được thêm vào.)


1

Bạn có thể sử dụng json_build_objectnhư thế này

SELECT 
  a.id, 
  a.name,
  json_agg(json_build_object('col1', b.col1, 'col2', b.col2) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
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.