2013-12-04 7 views
9

Mit postgresql 9.3 (und die neue json awesomness), wenn ich eine einfache Tabelle ‚Rassen‘ mit einer zweispaltigen Beschreibung genannt wie:Postgresql json anfragende in 9.3+ auf verschachtelte Nutzlasten

race-id integer, 
race-data json 

Und die json ist eine Nutzlast für jedes Rennen ist so etwas wie

{  "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 } 

Wie ich die Tabelle für abfragen kann:

1) Races wo sam 1. gekommen ist

2) Rennen, wo sam gekommen 1. und John hat 2nd kommen

3) Ist die Zahl der Läufer mit dem Alter größer als 30 ist> 5 und Preisgeld> 5000

Meine Experimente (vor allem in das Abfragen einer verschachtelten Array-Nutzlast) hat bisher zu einer weiteren Normalisierung der Daten geführt, dh zum Erstellen einer Tabelle mit der Bezeichnung Läufer, nur um solche Abfragen durchzuführen. Im Idealfall möchte ich diese neue fangled JSON Query awesomeness verwenden, aber ich kann nicht scheinen, Kopf oder Zahl davon in Bezug auf die 3 einfachen Abfragen zu machen.

Antwort

13

Sie können Abroller json in einen Datensatz und führen Sie dann Ihre Fragen, wie Sie wollen (siehe 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

Es ist ein bisschen kompliziert, weil Ihrer JSON Struktur. Ich glaube, dass, wenn Sie Ihre Struktur etwas ändern , Ihre Anfragen viel simplier sein könnte:

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

Sie verwenden können ->> und -> Operatoren oder json_extract_path_text Funktion benötigten Daten zu erhalten und es dann in der where-Klausel :

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