Passen Sie die Sortierreihenfolge für jsonb-Schlüssel mit Arrays an

9

Ich habe eine Tabelle in PostgreSQL mit einigen Daten:

create table t2 (
    key jsonb,
    value jsonb
);

INSERT INTO t2(key, value)
 VALUES
 ('1', '"test 1"')
,('2', '"test 2"')
,('3', '"test 3"')
,('[]', '"test 4"')
,('[1]', '"test 5"')
,('[2]', '"test 6"')
,('[3]', '"test 7"')
,('[1, 2]', '"test 8"')
,('[1, 2, 3]', '"test 9"')
,('[1, 3]', '"test 10"')
,('[1,2,4]', '"test 11"')
,('[1, 2,4]', '"test 12"')
,('[1,3,13]', '"test 13"')
,('[1, 2, 15]', '"test 15"');

Und ich versuche diese Zeilen so zu sortieren:

SELECT key FROM t2 order by key;

Das Ergebnis ist:

[]
1
2
3
[1]
[2] <==
[3] <==
[1, 2]
[1, 3] <==
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 15]
[1, 3, 13]

Aber was ich brauche ist:

[]
1
2
3
[1]
[1, 2]
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 15]
[1, 3] <==
[1, 3, 13]
[2] <==
[3] <==

Gibt es einen Weg, dies zu erreichen?

Antonio
quelle
Hast du hier deine Antwort?
Erwin Brandstetter

Antworten:

8

Zunächst einmal sind Ihre Frage sowie Ihr Spaltenname "key"irreführend. Der Spaltenschlüssel enthält keine JSON- Schlüssel , nur Werte . Sonst könnten wir die Funktion verwenden jsonb_object_keys(jsonb), um Schlüssel zu extrahieren, aber das ist nicht so.

Angenommen, alle Ihre JSON-Arrays sind entweder leer oder enthalten ganzzahlige Zahlen, wie gezeigt. Und die Skalarwerte (Nicht-Arrays) sind ebenfalls ganzzahlig.

Ihre grundlegende Sortierreihenfolge würde mit Postgres integer(oder numeric) Arrays funktionieren . Ich benutze diese kleine Hilfsfunktion , um jsonbArrays in Postgres zu konvertieren int[]:

CREATE OR REPLACE FUNCTION jsonb_arr2int_arr(_js jsonb)
   RETURNS int[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT j::int FROM jsonb_array_elements_text(_js) j)';

Erläuterung:

Fügen Sie dann hinzu jsonb_typeof(jsonb), um zu folgendem Ergebnis zu gelangen:

SELECT key
FROM   t2
ORDER  BY key <> '[]'             -- special case for empty array
        , jsonb_typeof(key) DESC  -- 'number' before 'array'
        , CASE jsonb_typeof(key)  -- sort arrays as converted int[]
            WHEN 'array'  THEN jsonb_arr2int_arr(key)
            WHEN 'number' THEN ARRAY[key::text::int]
          END;

Erzeugt genau das gewünschte Ergebnis.

Warum?

Das Handbuch für jsonberklärt:

Die btreeBestellung von jsonbBezugspunkten ist selten von großem Interesse, der Vollständigkeit halber jedoch:

Object > Array > Boolean > Number > String > Null
Object with n pairs > object with n - 1 pairs
Array with n elements > array with n - 1 elements

Objekte mit gleicher Anzahl von Paaren werden in der folgenden Reihenfolge verglichen:

key-1, value-1, key-2 ...

Beachten Sie, dass Objektschlüssel in ihrer Speicherreihenfolge verglichen werden. Insbesondere da kürzere Schlüssel vor längeren Schlüsseln gespeichert werden, kann dies zu Ergebnissen führen, die möglicherweise nicht intuitiv sind, wie z.

{ "aa": 1, "c": 1} > {"b": 1, "d": 1}

In ähnlicher Weise werden Arrays mit der gleichen Anzahl von Elementen in der folgenden Reihenfolge verglichen:

element-1, element-2 ...

Meine kühne Betonung.
Deshalb jsonb '[2]' < jsonb '[1, 2]'.
Aber Postgres-Arrays sortieren nur Element für Element: '{2}'::int[] > '{1, 2}'- genau das, wonach Sie gesucht haben.

Erwin Brandstetter
quelle
0

Beziehen Sie sich auf das Problem, um Ihre Ergebnisse nach json-Ganzzahlwerten zu ordnen. Versuchen:

select myjson from mytable order by (myjson->>'some_int')::int;

In Ihrem Fall scheint es sich um ein Array für den Bestellschlüssel zu handeln. Versuchen Sie also zuerst, die Werte in Ihrem "Schlüsselfeld" zu verknüpfen.

dlg_
quelle