2015-05-16 30 views
5

Nota: Sto usando l'ultima versione di Postgres (9,4)Come combinare DISTINCT e ORDER BY in array_agg di valori jsonb in PostgreSQL

Sto cercando di scrivere una query che fa una semplice unione di 2 tavoli, e gruppi dalla chiave primaria della prima tabella, e fa un array_agg di diversi campi nella seconda tabella che voglio essere restituito come oggetto. L'array deve essere ordinato per una combinazione di 2 campi negli oggetti JSON e anche univoco.

Finora, hanno escogitato la seguente:

SELECT 
    zoo.id, 
    ARRAY_AGG(
    DISTINCT ROW_TO_JSON(( 
     SELECT x 
     FROM ( 
     SELECT animals.type, animals.name 
    ) x 
    ))::JSONB 
    -- ORDER BY animals.type, animals.name 
) 
    FROM zoo 
    JOIN animals ON animals.zooId = zoo.id 
    GROUP BY zoo.id; 

Questo si traduce in una riga per ogni zoo, con un array aggregato di oggetti jsonb, uno per ogni animale, in modo univoco.

Tuttavia, non riesco a capire come anche ordinare questo dai parametri nella parte commentata del codice.

Se seleziono il distinto, posso ORDER BY campi originali, che funziona benissimo, ma poi ho duplicati.

+0

Potrebbe fornire alcuni dati di esempio e l'output desiderato? – Eggplant

risposta

2

Se si utilizza row_to_json(), si perdono i nomi delle colonne a meno che non si inserisca una riga digitata. Se "manualmente" costruire l'oggetto jsonb con json_build_object() utilizzando nomi espliciti poi si arriva di nuovo:

SELECT zoo.id, array_agg(za.jb) AS animals 
FROM zoo 
JOIN (
    SELECT DISTINCT ON (zooId, "type", "name") 
    zooId, json_build_object('animal_type', "type", 'animal_name', "name")::jsonb AS jb 
    FROM animals 
    ORDER BY zooId, jb->>'animal_type', jb->>'animal_name' 
    -- ORDER BY zooId, "type", "name" is far more efficient 
) AS za ON za.zooId = zoo.id 
GROUP BY zoo.id; 

È possibile ORDER BY gli elementi di un oggetto jsonb, come sopra indicato, ma (per quanto ne so) si non è possibile utilizzare DISTINCT su un oggetto jsonb. Nel tuo caso, ciò sarebbe piuttosto inefficiente (prima costruendo tutti gli oggetti jsonb, quindi eliminando i duplicati) e a livello aggregato è assolutamente impossibile con SQL standard. È possibile ottenere lo stesso risultato, tuttavia, applicando la clausola DISTINCT prima di costruire l'oggetto jsonb.

Inoltre, evitare l'uso di SQL key words come "tipo" e tipi di dati standard come "nome" come nomi di colonne. Entrambe sono parole chiave non riservate, pertanto è possibile utilizzare nei relativi contesti, ma in pratica i comandi potrebbero diventare davvero confusi. Si potrebbe, per esempio, avere uno schema, con un tavolo, una colonna in tale tabella, e un tipo di dati ogni denominato "tipo" e quindi si potrebbe ottenere questo:

SELECT type::type FROM type.type WHERE type = something; 

Mentre PostgreSQL graziosamente accettare questo, è semplice confondere e incline all'errore in ogni genere di situazioni più complesse. Puoi fare un lungo cammino citando due volte le parole chiave, ma è meglio evitarlo come identificatori.

+0

Poiché la query che ho fornito è in realtà un sottoinsieme di una query più ampia (che ho ridotto per semplicità nel porre la domanda), ho dimenticato che c'è un WHERE sui parametri del tavolo dello zoo. Per quanto riguarda la query che hai fornito, questo risolve il problema, ma non risponde alla domanda principale che ho avuto, ovvero se è possibile combinare DISTINCT e ORDER BY in un aggregato di valori JSONB creati (uno di entrambi sembra funzionare alla grande). Inoltre, puoi approfondire l'uso di "nome" e "tipo"? Si tratta di un problema nei postgres o solo in generale? – Philberg

Problemi correlati