自定义涉及数组的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

Answers:


8

首先,您的问题以及列名"key"都具有误导性。column键不包含任何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;

在您的情况下,它似乎是订购键的数组。因此,首先尝试在您的“键”字段中合并值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.