2013-12-04 8 views
9

Uso PostgreSQL 9.3 (e la nuova awesomness JSON) se ho una tabella semplice denominata 'razze' con una descrizione a due colonne, come:PostgreSQL JSON interrogazione in 9.3+ sui carichi di nidificati

race-id integer, 
race-data json 

E il JSON è un carico utile per ogni gara è qualcosa di simile

{  "race-time": some-date, 
    "runners": [ { "name": "fred","age": 30, "position": 1 }, 
       { "name": "john","age": 29, "position": 3 }, 
       { "name": "sam","age": 31, "position": 2 } ], 
    "prize-money": 200 } 

Come posso interrogare la tabella per:

1) gare in cui Sam è venuto prima

2) gare in cui Sam è venuto prima e John è giunto 2 °

3) Se il numero di corridori con l'età superiore a 30 è> 5 e premio in denaro> 5000

mio sperimentazione (in particolare in interrogando un payload dell'array nidificato) finora ha portato a normalizzare ulteriormente i dati, cioè creare una tabella chiamata runner solo per fare tali query. Idealmente mi piacerebbe usare questo nuovo fasullo quesito di query json ma non riesco a farne testa o croce in riferimento alle 3 semplici domande.

risposta

13

È possibile rilassarsi JSON in un record e poi fare le vostre domande, come si vuole (vedi json functions):

with cte as (
    select 
     race_id, 
     json_array_elements(r.race_data->'runners') as d, 
     (r.race_data->>'prize-money')::int as price_money 
    from races as r 
), cte2 as (
    select 
     race_id, price_money, 
     max(case when (d->>'position')::int = 1 then d->>'name' end) as name1, 
     max(case when (d->>'position')::int = 2 then d->>'name' end) as name2, 
     max(case when (d->>'position')::int = 3 then d->>'name' end) as name3 
    from cte 
    group by race_id, price_money 
) 
select * 
from cte2 
where name1 = 'sam' and name2 = 'john' 

sql fiddle demo

E 'un po' complicato a causa della vostra JSON struttura. Penso che se si modifica la struttura un po ', le vostre domande potrebbero essere molto più semplice:

{ 
    "race-time": some-date, 
    "runners": 
    { 
     "1": {"name": "fred","age": 30}, 
     "2": {"name": "sam","age": 31}, 
     "3": {"name": "john","age": 29} 
    }, 
    "prize-money": 200 
} 

è possibile utilizzare ->> e -> operatori o json_extract_path_text funzione per ottenere dati necessari e poi utilizzarlo nella clausola where :

select * 
from races as r 
where 
    r.race_data->'runners'->'1'->>'name' = 'sam'; 

select * 
from races as r 
where 
    json_extract_path_text(r.race_data, 'runners','1','name') = 'sam' and 
    json_extract_path_text(r.race_data, 'runners','2','name') = 'john'; 

select * 
from races as r 
where 
    (r.race_data->>'prize-money')::int > 100 and 
    (
     select count(*) 
     from json_each(r.race_data->'runners') 
     where (value->>'age')::int >= 30 
    ) >= 2 

sql fiddle demo

Problemi correlati