2016-05-13 11 views
1

ich eine jsonb Spalte mit Beispielinhalt haben wie folgt:Wählen Sie die Anzahl der Einträge in einem bestimmten Schlüssel für eine PostgreSQL JSONB Spalte

{"kay1": val1, "myMap": {"UniqueKey1": "UniqueValue1", "UniqueKey2": "UniqueValue2", "UniqueKey3": "UniqueValue3", "UniqueKey4": "UniqueValue4"}, "key2": {"key3": {"key4": "val4"}, "val3": {"key5": "val5"}} 

ich alle Zeilen finden möchten, wo Anzahl der Einträge in ‚myMap‘ größer sind als/gleich/kleiner als eine ganze Zahl - ich habe einige Millionen solcher Zeilen, daher wäre es hilfreich, wenn die Indizierung auch verwendet werden könnte!

Im obigen Beispiel gibt es 4 Einträge in 'myMap'. Für eine Abfrage wie "select * from myTable wo jsonb_key_length (myJsonbColumn -> 'myMap') = 4" sollte die obige Zeile zurückgegeben werden. [Angenommen, es gibt eine Funktion jsonb_key_length(), die die Länge des gegebenen json-Objekts zurückgibt]

Ich habe ähnliche Frage hier gefunden: Postgres json key count.

Aber es erfordert den Namen der Schlüssel, kann dies ohne Verwendung von Schlüsselnamen getan werden?

Lösung

Dank @jmelesky für seinen Vorschlag.

folgende Abfrage für mich gearbeitet:

SELECT id, count(elements) 
FROM (SELECT id, jsonb_object_keys(column -> 'myMap') AS elements 
     FROM myTable GROUP BY id 
    ) x 
GROUP BY id 

Einschließlich @ jmelesky Vorschlag

SELECT id, (SELECT count(*) 
      FROM (SELECT jsonb_object_keys(a->'myMap') 
        FROM test_json x where x.id = y.id 
       ) z 
      ) count 
FROM test_json y group by id; 

Gefunden eine andere, noch schnellere Lösung

SELECT id, ARRAY_LENGTH(ARRAY(SELECT jsonb_object_keys(column -> 'myMap')), 1) AS count 
FROM myTable 

Um Index verwenden:
Erstellen Sie eine Funktion:

CREATE OR REPLACE FUNCTION jsonb_object_keys_length(_j jsonb) 
RETURNS INT LANGUAGE SQL IMMUTABLE AS 
'SELECT ARRAY_LENGTH(ARRAY(SELECT jsonb_object_keys(column -> 'myMap')), 1)'; 

erstellen Index:

CREATE INDEX idx_myMapCount ON myTable (jsonb_object_keys_length(column -> 'myMap')); 

Verwenden Sie die Funktion in Abfrage:

SELECT id, jsonb_object_keys_length(column -> 'myMap') AS count 
FROM myTable 

Bitte vorschlagen, wenn Es gibt einen besseren Weg um diese Abfrage zu modellieren. Vielen Dank!

Antwort

4

Es gibt eine function namens json_object_keys, die wahrscheinlich der Schlüssel zu diesem Problem ist. Es nimmt ein Json-Objekt und gibt die Schlüssel als Zeilen einer Relation zurück.

=# create table test_json (a json); 
CREATE TABLE 
=# insert into test_json values ('{"kay1": 1, "myMap": {"UniqueKey1": "UniqueValue1", "UniqueKey2": "UniqueValue2", "UniqueKey3": "UniqueValue3", "UniqueKey4": "UniqueValue4"}, "key2": {"key3": {"key4": "val4"}, "val3": {"key5": "val5"}}}'); 
INSERT 0 1 
=# select json_object_keys(a) from test_json; 
json_object_keys 
------------------ 
kay1 
myMap 
key2 
(3 rows) 
=# select json_object_keys(a->'myMap') from test_json; 
json_object_keys 
------------------ 
UniqueKey1 
UniqueKey2 
UniqueKey3 
UniqueKey4 
(4 rows) 

Von dort können Sie nur, dass wickeln in einer Unterabfrage, etwa so:

=# select count(*) from (select json_object_keys(a->'myMap') from test_json) x; 
count 
------- 
    4 
(1 row) 

Edited hinzufügen: Es gibt eine jsonb Äquivalent (jsonb_object_keys), die identisch mit jsonb Werten arbeitet . Entschuldigung, ich tendiere dazu, meine Testfälle in Vanille Json zu machen.

+1

Vielen Dank für Ihre Antwort. Aber diese Abfrage funktioniert nicht wie erwartet, da ich mehrere Zeilen habe, und ich möchte diese Anzahl für jede Zeile finden. Für zB: id | Spalte 1 | {"kay1": val1, "myMap": {"Eindeutiger Schlüssel1": "EindeutigerWert1", "Eindeutiger Schlüssel2": "EindeutigerWert2", "Eindeutiger Schlüssel3": "EindeutigerWert3", "EindeutigerSchlüssel4": "EindeutigerWert4"}, "Schlüssel2": { "key3": {"key4": "val4"}, "val3": {"key5": "val5"}} 2 | {"kay1": val1, "myMap": {"Eindeutiger Schlüssel21": "EindeutigerWert21", "Eindeutiger Schlüssel22": "EindeutigerWert22"}, "Schlüssel2": {"Schlüssel3": {"Schlüssel4": "Wert4"}, "Wert3 ": {" key5 ":" val5 "}} Ich möchte etwas wie: id | myMap_count 1 | 4 2 | 2 –

+1

Etwas wie folgt aus: SELECT id, count (Elemente) FROM test_json tj, jsonb_each_text (tj.a -> myMap ') AS Elemente GROUP BY id Aber Abfragen anstelle der Verwendung 'von' –

+0

Es ist relativ einfach. Etwas wie 'select id, (wählen Sie count (*) from (wählen Sie jsonb_object_keys (a -> 'myMap') aus test_json x wobei x.id = y.id) z) zählen aus test_json y group nach id; – jmelesky

Verwandte Themen