Wybierz kolumny w pliku json_agg


21

Mam zapytanie takie jak:

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;

Jak mogę wybrać kolumny, baby nie mieć b.item_idobiektu JSON?

Czytałem o ROW, ale zwraca obiekt JSON, taki jak:

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

Musiałbym ponownie mapować obiekt JSON po jego pobraniu w celu dopasowania odpowiednich kluczy kolumny. Chciałbym tego uniknąć i zachować oryginalne nazwy kolumn.

Odpowiedzi:


50

Niestety w składni SQL nie ma przepisu mówiącego „wszystkie kolumny oprócz tej jednej kolumny” . Możesz osiągnąć swój cel, wypisując pozostałą listę kolumn w wyrażeniu typu wiersz :

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;

To skrót od bardziej zrozumiałej formie: . ROW(b.col1, b.col2, b.col3)

Jednak nazwy kolumn nie są zachowywane w wyrażeniach typu wierszowego. W ten sposób otrzymujesz ogólne nazwy kluczy w obiekcie JSON. Widzę 3 opcje zachowania oryginalnych nazw kolumn:

1. Przesyłaj do zarejestrowanego typu

Rzuć na dobrze znany (zarejestrowany) typ wiersza. Typ jest rejestrowany dla każdej istniejącej tabeli lub widoku lub z wyraźną CREATE TYPEinstrukcją. Możesz użyć tabeli tymczasowej dla rozwiązania ad-hoc (obowiązuje przez czas trwania sesji):

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. Użyj podselekcji

Użyj podselekcji, aby zbudować tabelę pochodną i odwołać się do tabeli jako całości . To także przenosi nazwy kolumn. Jest bardziej szczegółowy, ale nie potrzebujesz zarejestrowanego typu:

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()w Postgres 9.4 lub nowszym

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;

Związane z:

Podobnie w jsonbprzypadku odpowiednich funkcji jsonb_agg()i jsonb_build_object().

Dla PostgreSQL 9.5 lub nowszy również zobaczyć odpowiedź a_horse w nowym wariancie krótszy składni: Postgres dodał operator minus -zajsonb powiedzieć „wszystkie klawisze z wyjątkiem tego jednego klucza” .
Ponieważ Postgres 10 „oprócz kilku kluczy” jest implementowany z tym samym operatorem, który bierze text[]jako drugi operand - jak komentuje mlt.


1
> lub kilka klawiszy Zauważ, że json (b) -text [] jest dostępny od 10.
— mlt

Rozwiązanie 3 działało dla mnie jak urok!
— Luiz Fernando da Silva

17

Począwszy od wersji 9.6, możesz po prostu użyć -do usunięcia klucza z 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)przekształci cały wiersz, a - 'item_id'następnie usunie klucz o nazwie, item_idktórej wynik zostanie następnie zagregowany.


Wydaje się, że te nowe funkcje były tym, na co liczył PO. Dodałem link do mojej odpowiedzi.
— Erwin Brandstetter

Kiedy wypróbowałem wariant podselekcji, otrzymałem błąd związany z json_aggfunkcją:function json_agg(record) does not exist
— fraxture

@fraxture: wtedy nie używasz Postgres 9.6
— a_horse_w_na_nazwa

Rzeczywiście to był problem. Czy jest jakiś sposób na filtrowanie kolumn w wersji 9.2?
— fraxture

8

Możesz to zrobić bez grupowania za pomocą podkwerend

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;

zwroty

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

Ten artykuł od Johna Attena jest naprawdę interesujący i zawiera więcej szczegółów


2

Przekonałem się, że najlepiej jest utworzyć JSON, a następnie go zagregować. na przykład

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)

Uwaga: można to zrobić jako podzapytanie, jeśli nie lubisz CTE (lub masz problemy z wydajnością z powodu korzystania z niego).

Zauważ też, że jeśli będziesz często to robić, może być korzystne utworzenie funkcji do zawijania dla Ciebie par klucz-wartość, aby kod wyglądał na czystszy. Przekazałeś swoją funkcję (na przykład) 'ecks', 'x'i ona powróciłaby "ecks": "x".


1

Chociaż nadal nie ma sposobu, aby zrobić nic z zaznaczeniem wszystkich kolumn oprócz jednego bitu, ale możesz użyć, json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))aby uzyskać tablicę jsonów jsonów w formacie {"col_1":"col_1 value", ...}.

Tak więc zapytanie wyglądałoby mniej więcej tak:

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;

i zwraca wiersze jako:

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

(Jestem teraz na Postgres 9.5.3 i nie jestem w 100% pewien, kiedy ta obsługa została dodana).


1

Możesz użyć w json_build_objectten sposób

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;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.