2012-12-11 10 views
5

Sto utilizzando array_to_json in combinazione con array_agg per formattare determinati risultati in PostgreSQL come JSON. Funziona bene per le query in cui voglio restituire il valore predefinito di una query (tutte le colonne, non modificato). Ma sono perplesso su come potrei usare array_agg per creare un oggetto JSON per una query in cui voglio modificare parte dell'output.Applicazione array_agg/array_to_json a una query con colonne modificate

Ecco un esempio. Diciamo che abbiamo la seguente:

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'); 

la seguente query funziona bene:

# 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}] 

Ma diciamo che non mi piace il valore null che appare per l'utente 3. Preferisco vedere la stringa "Utente connesso da $ ip" invece.

posso fare questo:

# 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; 

E io ottenere i seguenti risultati:

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

che è grande. Ma non riesco a capire come manipolare questi dati nel formato JSON come il primo esempio.

L'uscita desiderata è ovviamente il seguente:

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

Il seguente non funziona:

# 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 

non riesco a capire un modo per ottenere i dati in un formato che array_agg accetta. Ho persino provato a creare un tipo personalizzato che corrispondesse al formato di temp_user e che cercasse di eseguire le chiamate array_agg al costruttore del tipo, che restituiva lo stesso errore. L'errore non ha senso per me - se la subquery è aggregata, allora non dovrebbe importare se restituisce più di una colonna. Qualche consiglio?

risposta

10

È possibile separare la chiamata aggregato dalla subquery e utilizzare il costruttore row per generare i dati composto:

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 
; 

È inoltre possibile controllare questo su SQLFiddle.

+1

Posso confermare questo funziona; Ho appena applicato questo metodo correttamente alla mia domanda reale. Grazie! – hs0

+7

La query può essere semplificata un po 'sostituendo 'array_agg (row (t. *))' Semplicemente 'array_agg (t)'. In questo modo manterrai anche i nomi delle colonne dalla sottoquery. –

2

Solo un aggiornamento qui, postgres ora ha una funzione json_agg che può essere utilizzato in leiu di array_to_json(array_agg(...)) ma in realtà si comporta meglio in alcuni casi, vedere qui: Array_agg in postgres selectively quotes

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

ecco la modifica query:

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 è super potente! – nicodjimenez

Problemi correlati