Json_agg içindeki sütunları seçme


21

Gibi bir sorgu var:

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;

JSON nesnesinde bolmamak için sütunları nasıl seçebilirim b.item_id?

Hakkında okudum ROW, ama gibi bir JSON nesnesi döndürür:

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

Bir kez uygun sütun anahtarları eşleştirmek için getirilir JSON nesnesini yeniden gerekir. Bundan kaçınmak ve orijinal sütun adlarını saklamak istiyorum.

Yanıtlar:


50

Ne yazık ki, SQL sözdiziminde söyleyecek bir hüküm yok "bu bir sütun dışındaki tüm sütunlar" . Bir satır türü ifadesinde kalan sütun listesini yazarak hedefinize ulaşabilirsiniz :

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;

Yani daha açık formda kısaltılmışı: . ROW(b.col1, b.col2, b.col3)

Ancak, sütun adları satır türü ifadelerinde korunmaz. JSON nesnesinde genel anahtar adlarını bu şekilde alırsınız. Orijinal sütun adlarını korumak için 3 seçenek görüyorum:

1. Kayıtlı türe yayınla

İyi bilinen (kayıtlı) bir satır türüne yayınlayın. Bir tür, mevcut her tablo veya görünüm için veya açık bir şekilde kaydedilirCREATE TYPE ifadeyle kaydedilir. Geçici bir çözüm için geçici bir tablo kullanabilirsiniz (oturum süresi boyunca geçerlidir):

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. Bir alt seçim kullanın

Türetilmiş bir tablo oluşturmak ve tabloya bir bütün olarak başvurmak için bir alt seçim kullanın . Bu aynı zamanda sütun isimlerini de taşır. Daha ayrıntılı, ancak kayıtlı bir türe ihtiyacınız yok:

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 veya sonrasında

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şkili:

jsonbİlgili işlevlerle benzer jsonb_agg()vejsonb_build_object() .

İçin Postgres 9.5 ayrıca bkz veya daha sonra a_horse cevabını yeni kısa sözdizimi varyantı ile: Postgres eklendi eksi operatörü -içinjsonb söylenecek "bu bir tuşu dışındaki tüm tuşlar" .
Yana Postgres 10 "birkaç tuşları dışında" aynı operatör alma ile uygulanan text[]2 işlenen olarak - mlt yorumladı gibi.


1
> veya birkaç tuş json (b) -text [] öğesinin 10'dan başlayarak kullanılabildiğini unutmayın.
mlt

Çözüm 3 benim için bir cazibe gibi çalıştı!
Luiz Fernando da Silva

17

9.6 ile başlayarak -JSONB'den bir anahtarı kaldırmak için kullanabilirsiniz :

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)satırın tamamını dönüştürür ve - 'item_id'ardından anahtarın item_idsonucunun toplandığı adla birlikte anahtarını kaldırır .


Bu yeni özellikler OP'nin umduğu şey gibi görünüyor. Cevabıma bir link ekledim.
Erwin Brandstetter

json_aggfunction json_agg(record) does not exist
Subselect

@fraxture: o zaman Postgres 9.6 kullanmıyorsunuz
a_horse_with_no_name

Gerçekten de sorun buydu. V9.2'de sütunları filtrelemenin bir yolu var mı?
fraxture

8

Bunu aslında alt sorgular kullanarak gruplandırma olmadan yapabilirsiniz

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;

İadeler

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

John Atten'in bu makalesi gerçekten ilginç ve daha fazla ayrıntıya sahip


2

JSON'u oluşturmak, sonra toplamak en iyisi olduğunu buldum. Örneğin

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'leri sevmiyorsanız (veya kullandığınız için performans sorunlarınız varsa) bunun bir alt sorgu olarak yapılabileceğini unutmayın.

Ayrıca, bunu çok fazla yapacaksanız, kodun daha temiz görünmesi için anahtar / değer çiftlerini sizin için sarmak üzere bir işlev oluşturmak yararlı olabilir. İşlevi (örneğin) geçirdiniz 'ecks', 'x've geri dönecekti "ecks": "x".


1

Yine de tüm sütunları seçmek için bir bit yapmanın bir yolu olmasa da, bir bit json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))json dizisi almak için kullanabilirsiniz {"col_1":"col_1 value", ...}.

Böylece sorgu şöyle görünecektir:

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;

ve satırları şu şekilde döndürür:

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

(Şimdi Postgres 9.5.3 kullanıyorum ve bu desteğin eklendiğinde% 100 emin değilim.)


1

Sen kullanabilirsiniz json_build_objectBöyle

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.