如何通过对象的属性对json或jsonb值内的数组中的对象进行排序?

Hal*_*yon 2 postgresql json sql-order-by plpgsql jsonb

我有这个pl/pgsql函数来聚合jsonb值(data_table_1data_table_2)中两个表的行.fk_id是两个表中的常见外键ID:

DECLARE
v_my_variable_1 jsonb;
v_my_variable_2 jsonb;
v_combined      jsonb;
BEGIN
  SELECT json_agg( data_table_1 ) INTO v_my_variable FROM data_table_1 WHERE fk_id = v_id;
  SELECT json_agg( data_table_2 ) into v_my_variable_2 FROM data_table_2 WHERE fk_id = v_id;
  SELECT v_my_variable || v_my_variable_2 into v_combined;
Run Code Online (Sandbox Code Playgroud)

现在我想v_combined按字段排序,这ts是两个表共有的时间戳列,因此是jsonb值中所有数组对象的公共键.

例:

v_combined = '[{"id": 1, "type": 4, "param": 3, "ts": 12354355}
             , {"id": 1, "txt": "something", "args": 5, "ts": 12354345}]';
Run Code Online (Sandbox Code Playgroud)

如何v_combined按升序对数组元素进行排序ts

如果我从桌子上选择,我可以简单地使用:

select * into v_combined from v_combined ORDER BY v_combined->>'ts' ASC;
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试时,它说不v_combined存在.有没有办法将它存储在临时表中并在那里进行排序,还是有直接的方法来对pl/pgsql中的json对象数组进行排序?

Erw*_*ter 7

键的对象顺序jsonb文字是微不足道的-对象键在内部反正排序.(json在这方面有所不同.)见:

数组元素的顺序在一个jsonb(或json)文字是显著,虽然.您的要求很有意义.你可以像这样重新排序:

SELECT jsonb_agg(elem)
FROM  (
   SELECT *
   FROM   jsonb_array_elements(v_combined) a(elem)
   ORDER  BY (elem->>'ts')::int  -- order by integer value of "ts"
   ) sub;
Run Code Online (Sandbox Code Playgroud)

dbfiddle 在这里

但是分配数组之前对它进行排序会更有效:

...
DECLARE
   v_combined      jsonb;
BEGIN
   SELECT INTO v_combined  jsonb_agg(elem)
   FROM  (
      SELECT ts, json_agg(data_table_1) AS j
      FROM   data_table_1
      WHERE  fk_id = v_id

      UNION ALL 
      SELECT ts, json_agg(data_table_2)
      FROM   data_table_2
      WHERE  fk_id = v_id
      ORDER  BY ts
      ) sub;
...
Run Code Online (Sandbox Code Playgroud)

在子查询中的行的顺序

在标准SQL中,子查询(或任何表表达式)中的行顺序也是无关紧要的.但是在Postgres中,子查询中的行顺序会延续到下一个级别.所以这适用于简单的查询.它甚至记录在案:

...从排序的子查询中提供输入值通常会起作用.例如:

SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
Run Code Online (Sandbox Code Playgroud)

请注意,如果外部查询级别包含额外的处理,如加入这个方法可能会失败,因为这可能导致计算的总前将重新排列子查询的输出.

如果您不能或不会依赖于此,则有一个安全的替代方法:ORDER BY向聚合函数本身添加一个.那甚至更短:

SELECT INTO v_combined  jsonb_agg(elem  ORDER BY (elem->>'ts')::int)
FROM   jsonb_array_elements(v_combined) a(elem);
Run Code Online (Sandbox Code Playgroud)

但它通常较慢.