Налаштувати порядок сортування ключів jsonb, що включає масиви


9

У мене є таблиця в PostgreSQL з деякими даними:

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"');

І я намагаюся сортувати такі рядки так:

SELECT key FROM t2 order by key;

Результат:

[]
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]

Але мені потрібно:

[]
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] <==

Чи є спосіб досягти цього?


То ти маєш тут свою відповідь?
Erwin Brandstetter

Відповіді:


8

По-перше, ваше питання, а також назва стовпця "key"вводять в оману. Ключ стовпця не містить жодних ключів JSON , лише значення . В іншому випадку ми могли використовувати функцію jsonb_object_keys(jsonb)для вилучення ключів, але це не так.

Якщо припустити, що всі ваші масиви JSON є порожніми або містять цілі числа, як показано. І скалярні значення (не масиви) також цілі.

Ваш основний порядок сортування працюватиме з масивами Postgres integer(або numeric). Я використовую цю маленьку помічну функцію для перетворення jsonbмасивів у Postgres 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)';

Пояснення:

Потім додайте, jsonb_typeof(jsonb)щоб прибути до:

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;

Точно дає бажаний результат.

Чому?

Посібник для jsonbпояснення:

btreeЗамовлення для jsonbдатум рідко представляє великий інтерес, але для повноти це:

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

Об'єкти з рівним числом пар порівнюються в порядку:

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

Зауважте, що об’єктні ключі порівнюються в порядку їх зберігання; зокрема, оскільки короткі клавіші зберігаються перед довшими клавішами, це може призвести до неінтуїтивних результатів, таких як:

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

Аналогічно, масиви з однаковою кількістю елементів порівнюються в порядку:

element-1, element-2 ...

Сміливий акцент мій.
Ось чому jsonb '[2]' < jsonb '[1, 2]'.
Але масиви Postgres просто сортують по елементах: '{2}'::int[] > '{1, 2}'- саме те, що ви шукали.


0

Посилаючись на проблему, щоб упорядкувати результати за цілими значеннями json. Спробуйте:

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

У вашому випадку, здається, це масив для ключа замовлення. Тому спочатку спробуйте визначити значення у вашому полі "ключ".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.