2012-12-11 7 views
5

Używam array_to_json w połączeniu z array_agg do formatowania określonych wyników w PostgreSQL jako JSON. Działa to dobrze dla zapytań, w których chcę zwrócić domyślną wartość zapytania (wszystkie kolumny, niezmodyfikowane). Ale jestem zakłopotany, jak mogę użyć array_agg do utworzenia obiektu JSON dla zapytania, w którym chcę zmodyfikować niektóre dane wyjściowe.Stosowanie array_agg/array_to_json do zapytania z zmodyfikowanymi kolumnami

Oto przykład. Powiedzmy, że mamy następujące:

CREATE TABLE temp_user ( 
    user_id serial PRIMARY KEY, 
    real_name text 
); 
CREATE TABLE temp_user_ip (
    user_id integer, 
    ip_address text 
); 
INSERT INTO temp_user (user_id, real_name) VALUES (1, 'Elise'), (2, 'John'), (3, NULL); 
INSERT INTO temp_user_ip (user_id, ip_address) VALUES (1, '10.0.0.4'), (2, '10.0.0.7'), (3, '10.0.0.9'); 

Następująca kwerenda działa prawidłowo:

# SELECT array_to_json(array_agg(temp_user)) as users from temp_user; 
              users             
----------------------------------------------------------------------------------------------------- 
[{"user_id":1,"real_name":"Elise"},{"user_id":2,"real_name":"John"},{"user_id":3,"real_name":null}] 

Ale powiedzmy, że nie podoba mi się wartość zerową dla użytkownika pojawiające 3. Wolałbym zobaczyć zamiast tego pojawi się ciąg "Użytkownik zalogowany z $ ip".

mogę to zrobić:

# SELECT user_id, (CASE WHEN real_name IS NULL THEN (select 'User logged in from ' || ip_address FROM temp_user_ip WHERE user_id = temp_user.user_id) ELSE real_name END) as name from temp_user; 

I uzyskać następujące wyniki:

user_id |    name    
---------+------------------------------ 
     1 | Elise 
     2 | John 
     3 | User logged in from 10.0.0.9 

co jest dobre. Ale nie mogę wymyślić, jak manipulować tymi danymi w formacie JSON, jak pierwszy przykład.

Pożądany wynik jest oczywiście następuje:

[{"user_id":1,"name":"Elise"},{"user_id":2,"name":"John"},{"user_id":3,"name":"User logged in from 10.0.0.9"}] 

Następujące nie działa:

# select array_to_json(array_agg ((SELECT user_id, (CASE WHEN real_name IS NULL THEN (select 'User logged in from ' || ip_address FROM temp_user_ip WHERE user_id = temp_user.user_id) ELSE real_name END) as name from temp_user))); 
ERROR: subquery must return only one column 

nie mogę dowiedzieć się, w dowolny sposób, aby uzyskać dane do formatu array_agg akceptuje. Próbowałem nawet utworzyć niestandardowy typ, który pasował do formatu temp_user i próbował wywoływać array_agg do konstruktora typu, który zwrócił ten sam błąd. Błąd nie ma dla mnie sensu - jeśli podzapytanie jest zagregowane, to nie powinno mieć znaczenia, czy zwraca więcej niż jedną kolumnę. Jakakolwiek rada?

Odpowiedz

10

można rozdzielić łączną połączenie z podzapytania i użyć konstruktora row do generowania danych związek:

SELECT 
    array_to_json(array_agg(row(t.*))) AS users 
FROM 
    (
     SELECT user_id, 
      CASE 
       WHEN real_name IS NULL 
       THEN (
        SELECT 'User logged in from ' || ip_address 
        FROM temp_user_ip 
        WHERE user_id = temp_user.user_id 
       ) ELSE real_name 
      END AS name 
     FROM temp_user 
    ) t 
; 

Można również sprawdzić to na SQLFiddle.

+1

Potwierdzam, że to działa; Właśnie zastosowałem tę metodę pomyślnie do mojego prawdziwego zapytania. Dzięki! – hs0

+7

Zapytanie można nieco uprościć, zastępując 'array_agg (wiersz (t. *))' Po prostu 'array_agg (t)'. W ten sposób zachowujesz również nazwy kolumn z podzapytania. –

2

właśnie aktualizacja tutaj, postgres tej pory funkcja json_agg, które mogą być stosowane w leiu z array_to_json(array_agg(...)) ale faktycznie zachowuje się lepiej w niektórych przypadkach, zobacz tutaj: Array_agg in postgres selectively quotes

Docs: https://www.postgresql.org/docs/9.5/static/functions-aggregate.html

tutaj jest modyfikowana zapytanie:

SELECT 
    json_agg(row(t.*)) AS users 
FROM 
    (
     SELECT user_id, 
      CASE 
       WHEN real_name IS NULL 
       THEN (
        SELECT 'User logged in from ' || ip_address 
        FROM temp_user_ip 
        WHERE user_id = temp_user.user_id 
       ) ELSE real_name 
      END AS name 
     FROM temp_user 
    ) t 
; 
+0

wow json_agg jest super potężny! – nicodjimenez