10

I have a table in PostgreSQL with some data:

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

And I try to sort these rows like that:

SELECT key FROM t2 order by key;

The result is:

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

But what I need is:

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

Is there a way to achieve it?

Erwin Brandstetter
186k28 gold badges463 silver badges636 bronze badges
asked Jun 23, 2016 at 16:51
1
  • So do you have your answer? Commented Dec 27, 2020 at 5:19

2 Answers 2

8

First off, your question as well as your column name "key" are misleading. The column key does not contain any JSON keys, only values. Else we could use the function jsonb_object_keys(jsonb) to extract keys, but that's not so.

Assuming all your JSON arrays are either empty or hold integer numbers as demonstrated. And the scalar values (non-arrays) are also integer.

Your basic sort order would work with Postgres integer (or numeric) arrays. I use this little helper function to convert jsonb arrays to 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)';

Explanation:

Then add jsonb_typeof(jsonb) to arrive at:

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;

Produces the desired result exactly.

Why?

The manual for jsonb explains:

The btree ordering for jsonb datums is seldom of great interest, but for completeness it is:

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

Objects with equal numbers of pairs are compared in the order:

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

Note that object keys are compared in their storage order; in particular, since shorter keys are stored before longer keys, this can lead to results that might be unintuitive, such as:

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

Similarly, arrays with equal numbers of elements are compared in the order:

element-1, element-2 ...

Bold emphasis mine.
That's why jsonb '[2]' < jsonb '[1, 2]'.
But Postgres arrays just sort element-by-element: '{2}'::int[] > '{1, 2}' - exactly what you were looking for.

answered Jun 24, 2016 at 2:22
0
0

Refering to the problem to order your results by json integer values. Try:

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

In your case it seems to be an array for the ordering key. So first try to concat the values in your "key" field.

Tom V
15.8k7 gold badges66 silver badges87 bronze badges
answered Nov 28, 2016 at 15:58

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.