2017-10-27 3 views
2

I Postgresql verwenden 9.6 zur Zeit und versucht seit Stunden die folgenden gelöst auf die effizienteste Art und Weise zu erhalten:Postgresql 9.6: Abfrage bestimmte Bereiche eines JSONB Array

Mein Beispiel Tabelle:

id | data 
----------------------------------------------------------------------------------------------------------- 
10 | {"scores": [{"u": "Peter", s: 120}, {"u": "Joe", s: 100}, {"u": "Pam", s: 70}, {"u": "Lisa", s: 120}]} 
11 | {"scores": [{"u": "Mae", s: 320}, {"u": "Max", s: 230}, {"u": "Moe", s: 170}, {"u": "Mini", s: 120}]} 
12 | {"scores": [{"u": "Jack", s: 140}, {"u": "John", s: 110}, {"u": "Wes", s: 70}, {"u": "Mick", s: 20}]} 

I benötigen eine Abfrage, um die ersten 2 Benutzernamen (u) jeder Zeile zu erhalten.

So sollte mein Ergebnis so aussehen:

users 
----- 
Peter 
Joe 
Mae 
Max 
Jack 
John 

Und eine Abfrage, um die 2. und 3. Benutzernamen zu erhalten:

users 
----- 
Joe 
Pam 
Max 
Moe 
John 
Wes 

Was der effizienteste Weg sein würde, dieses Problem zu lösen? Meine realen Tabellen haben Arrays mit 100-400 Elementen und etwa 1500 Zeilen.

+1

Wenn Sie dies oft machen müssen, dann denken Sie vielleicht an den Ratschlag in [dem Handbuch] (https://www.postgresql.org/docs/current/static/arrays.html) bezüglich normaler Arrays: "* Arrays sind keine Sätze; Die Suche nach bestimmten Array-Elementen kann ein Anzeichen für ein falsches Datenbankdesign sein. Erwägen Sie, für jedes Element, das ein Array-Element wäre, eine separate Tabelle mit einer Zeile zu verwenden. Dies wird einfacher zu suchen sein und wird wahrscheinlich besser skalieren für eine große Anzahl von Elementen * ". Dies gilt auch für Arrays im JSON-Objekt –

Antwort

3

Erste 2-Benutzernamen:

SELECT scores -> 'u' 
FROM (
    SELECT ROW_NUMBER() OVER(PARTITION BY id) rn, scores 
    FROM (
    SELECT id, data, jsonb_array_elements(data -> 'scores') AS scores 
    FROM scores 
) s1 
) s2 
WHERE rn <= 2; 

2. und 3. Benutzernamen:

SELECT scores -> 'u' 
FROM (
    SELECT ROW_NUMBER() OVER(PARTITION BY id) rn, scores 
    FROM (
    SELECT id, data, jsonb_array_elements(data -> 'scores') AS scores 
    FROM scores 
) s1 
) s2 
WHERE rn = 2 OR rn = 3; 

Eine andere Möglichkeit wäre:

SELECT username 
FROM (
    SELECT unnest(ARRAY [(data #> '{scores,0,u}') :: TEXT, (data #> '{scores,1,u}') :: TEXT]) username 
    FROM scores 
) usernames WHERE username NOTNULL; 

2. und 3. Benutzernamen:

SELECT username 
FROM (
    SELECT unnest(ARRAY [(data #> '{scores,1,u}') :: TEXT, (data #> '{scores,2,u}') :: TEXT]) username 
    FROM scores 
) usernames WHERE username NOTNULL; 
+0

Awesome! Danke - wirklich geholfen –

+0

Danke @Tomer das war genau das, was ich vermisste . – Smrchy