it-swarm.com.de

Abfrage von JSONB in ​​PostgreSQL

Ich habe eine Tabelle, persons, die zwei Spalten enthält, eine id und eine JSONB-basierte data -Spalte (diese Tabelle wurde nur zu Demonstrationszwecken erstellt, um damit herumzuspielen JSON-Unterstützung von PostgreSQL).

Angenommen, es enthält zwei Datensätze:

1, { name: 'John', age: 30 }
2, { name: 'Jane', age: 20 }

Angenommen, ich möchte den Namen jeder Person erfahren, die älter als 25 Jahre ist. Was ich versucht habe, ist:

select data->'name' as name from persons where data->'age' > 25

Dies führt leider zu einem Fehler. Ich kann es lösen, indem ich ->> Anstelle von -> Verwende, aber dann funktionieren Vergleiche nicht mehr wie erwartet, da nicht die Zahlen verglichen werden, sondern ihre Darstellungen als Zeichenfolgen:

select data->'name' as name from persons where data->>'age' > '25'

Ich habe dann herausgefunden, dass ich das Problem tatsächlich lösen kann, indem ich -> Und eine Besetzung für int verwende:

select data->'name' as name from persons where cast(data->'age' as int) > 25

Das funktioniert, aber es ist nicht so schön, dass ich den tatsächlichen Typ kennen muss (der Typ von age im JSON-Dokument ist sowieso number, also warum kann PostgreSQL das nicht selbst herausfinden? ?).

Ich habe dann herausgefunden, dass, wenn ich mit der Syntax :: Manuell in text konvertiere, auch alles wie erwartet funktioniert - obwohl wir jetzt wieder Zeichenfolgen vergleichen.

select data->'name' as name from persons where data->'age'::text > '25'

Wenn ich das dann mit dem Namen anstelle des Alters versuche, funktioniert es nicht:

select data->'name' as name from persons where data->'name'::text > 'Jenny'

Dies führt zu einem Fehler:

ungültige Eingabesyntax für Typ json

Ganz offensichtlich bekomme ich hier nichts. Leider ist es ziemlich schwierig, Beispiele für die Verwendung von JSON mit PostgreSQL in der Praxis zu finden.

Irgendwelche Hinweise?

13
Golo Roden

Dies funktioniert nicht, da versucht wird, einen jsonb -Wert in integer umzuwandeln.

select data->'name' as name from persons where cast(data->'age' as int) > 25

Dies würde tatsächlich funktionieren:

SELECT data->'name' AS name FROM persons WHERE cast(data->>'age' AS int) > 25;

Oder kürzer:

SELECT data->'name' AS name FROM persons WHERE (data->>'age')::int > 25;

Und das:

SELECT data->'name' AS name FROM persons WHERE data->>'name' > 'Jenny';

Scheint wie eine Verwechslung mit den beiden Operatoren -> Und ->> und Operatorrang . Die Besetzung :: Bindet stärker als die Operatoren json (b).

Typ dynamisch herausfinden

Dies ist der interessantere Teil Ihrer Frage:

die Art des Alters im JSON-Dokument ist sowieso die Zahl. Warum kann PostgreSQL das nicht selbst herausfinden?

SQL ist eine streng typisierte Sprache. Es erlaubt nicht, dass derselbe Ausdruck in einer Zeile zu integer und in der nächsten zu text ausgewertet wird. Da Sie jedoch nur am boolean -Ergebnis des Tests interessiert sind, können Sie diese Einschränkung mit einem CASE -Ausdruck umgehen, der sich abhängig vom Ergebnis von jsonb_typeof() :

SELECT data->'name'
FROM   persons
WHERE  CASE jsonb_typeof(data->'age')
        WHEN 'number'  THEN (data->>'age')::numeric > '25' -- treated as numeric
        WHEN 'string'  THEN data->>'age' > 'age_level_3'   -- treated as text
        WHEN 'boolean' THEN (data->>'age')::bool           -- use boolean directly (example)
        ELSE FALSE                                         -- remaining: array, object, null
       END;

Ein untypisiertes String-Literal rechts vom Operator > Wird automatisch zum jeweiligen Typ des Werts links gezwungen. Wenn Sie dort einen typisierten Wert eingeben, muss der Typ übereinstimmen oder explizit umgewandelt werden - es sei denn, im System ist eine angemessene implizite Umwandlung registriert.

Wenn Sie wissen dass alle numerisch Werte tatsächlich integer sind, können Sie auch:

... (data->>'age')::int > 25 ...
14