เลือกคอลัมน์ภายใน json_agg


21

ฉันมีคำถามเช่น:

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;

ฉันจะเลือกคอลัมน์ในbดังนั้นฉันไม่ได้มีb.item_idในวัตถุ JSON ได้อย่างไร

ฉันได้อ่านเกี่ยวกับROWแต่มันกลับวัตถุ JSON เช่น:

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

ฉันจะต้องทำการแมปวัตถุ JSON อีกครั้งเมื่อมีการดึงข้อมูลเพื่อให้ตรงกับคีย์คอลัมน์ที่เหมาะสม ฉันต้องการหลีกเลี่ยงปัญหานั้นและเก็บชื่อคอลัมน์เดิมไว้

คำตอบ:


50

แต่น่าเสียดายที่ไม่มีบทบัญญัติในไวยากรณ์ SQL ที่จะบอกว่า"คอลัมน์ทั้งหมดยกเว้นหนึ่งคอลัมน์นี้" คุณสามารถบรรลุเป้าหมายของคุณโดยการสะกดรายการคอลัมน์ที่เหลืออยู่ในนิพจน์ประเภทแถว :

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;

นั่นคือสั้นสำหรับรูปแบบที่ชัดเจนมากขึ้น: ROW(b.col1, b.col2, b.col3)

อย่างไรก็ตามชื่อคอลัมน์จะไม่ถูกสงวนไว้ในนิพจน์ประเภทแถว คุณได้รับชื่อคีย์ทั่วไปในวัตถุ JSON ด้วยวิธีนี้ ฉันเห็น 3 ตัวเลือกเพื่อรักษาชื่อคอลัมน์เดิม:

1. ส่งไปยังประเภทที่ลงทะเบียน

ส่งไปยังประเภทแถวที่รู้จักกันดี (ลงทะเบียน) ประเภทถูกลงทะเบียนสำหรับทุกตารางหรือมุมมองที่มีอยู่หรือด้วยCREATE TYPEคำสั่งที่ชัดเจน คุณอาจใช้ตารางชั่วคราวสำหรับโซลูชันเฉพาะกิจ (ใช้งานได้ตลอดระยะเวลาของเซสชัน):

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. ใช้การเลือกย่อย

ใช้ subselect เพื่อสร้างตารางที่ได้มาและการอ้างอิงตารางเป็นทั้ง นอกจากนี้ยังมีชื่อคอลัมน์ มันละเอียดมากขึ้น แต่คุณไม่จำเป็นต้องมีประเภทที่ลงทะเบียน:

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()ใน Postgres 9.4 หรือใหม่กว่า

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;

ที่เกี่ยวข้อง:

สำหรับที่คล้ายกันjsonbกับฟังก์ชั่นที่เกี่ยวข้องและ jsonb_agg()jsonb_build_object()

สำหรับPostgres 9.5หรือในภายหลังยังเห็นคำตอบของ a_horseกับใหม่ที่แตกต่างไวยากรณ์สั้น: Postgres เพิ่มผู้ประกอบการลบ-สำหรับการjsonbที่จะบอกว่า"คีย์ทั้งหมดยกเว้นนี้หนึ่งที่สำคัญ"
เนื่องจากPostgres 10 "ยกเว้นหลายคีย์"ถูกนำไปใช้กับตัวดำเนินการเดียวกันtext[]กับตัวถูกดำเนินการตัวที่ 2 - เหมือน mlt แสดงความคิดเห็น


1
> หรือปุ่มหลาย ๆ อันโปรดทราบว่า json (b) -text [] สามารถใช้ได้ตั้งแต่ 10
mlt

ทางออกที่ 3 เหมาะกับฉันเหมือนมีเสน่ห์!
Luiz Fernando da Silva

17

เริ่มต้นด้วย 9.6 คุณสามารถใช้-เพื่อลบคีย์ออกจาก 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)จะแปลงทั้งแถว- 'item_id'แล้วจะลบคีย์ด้วยชื่อitem_idผลลัพธ์ของการรวมนั้น


คุณสมบัติใหม่นี้ดูเหมือนจะเป็นสิ่งที่ OP หวังไว้ ฉันเพิ่มลิงค์ไปยังคำตอบของฉัน
Erwin Brandstetter

เมื่อฉันลองชุดย่อยเลือกฉันมีข้อผิดพลาดเกี่ยวกับjson_aggฟังก์ชัน:function json_agg(record) does not exist
fraxture

@fraxture: คุณไม่ได้ใช้ Postgres 9.6
a_horse_with_no_name

แน่นอนว่าเป็นปัญหา มีวิธีกรองคอลัมน์ใน v9.2 หรือไม่?
fraxture

8

จริงๆคุณสามารถทำได้โดยไม่ต้องกลุ่มโดยใช้แบบสอบถามย่อย

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;

ผลตอบแทน

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

บทความจาก John Attenนี้น่าสนใจมากและมีรายละเอียดเพิ่มเติม


2

ฉันพบว่ามันเป็นการดีที่สุดที่จะสร้าง JSON แล้วรวมมัน เช่น

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)

หมายเหตุสิ่งนี้สามารถทำได้เป็นแบบสอบถามย่อยหากคุณไม่ชอบ CTE (หรือมีปัญหาด้านประสิทธิภาพเนื่องจากการใช้งาน)

โปรดทราบว่าหากคุณกำลังจะทำสิ่งนี้มากมันอาจเป็นประโยชน์ในการสร้างฟังก์ชั่นเพื่อห่อคู่คีย์ - ค่าสำหรับคุณเพื่อให้โค้ดดูสะอาดตา คุณจะผ่านการทำงานของคุณ (ตัวอย่าง) และมันก็จะกลับมา'ecks', 'x'"ecks": "x"


1

ในขณะที่ยังคงไม่มีวิธีทำอะไรเกี่ยวกับการเลือกคอลัมน์ทั้งหมด แต่หนึ่งบิต แต่คุณสามารถใช้ json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))เพื่อให้ได้อาร์เรย์ JSON ของ jsons {"col_1":"col_1 value", ...}ในแต่ละรูปแบบ

ดังนั้นแบบสอบถามจะมีลักษณะดังนี้:

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;

และจะคืนค่าแถวเป็น:

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"...},...]
...

(ฉันใช้ Postgres 9.5.3 แล้วและไม่แน่ใจ 100% เมื่อมีการเพิ่มการสนับสนุนนี้)


1

คุณสามารถใช้json_build_objectสิ่งนี้

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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.