根据 JSONB 第一个值按查询排序分组

问题描述

我正在使用 Postgresql 并且我有以下 sql 查询

    WITH A AS
(
    SELECT *,jsonb_array_elements(event_days) as event_days_arr
    FROM event
)
SELECT event_id::int,MAX(area_id)::int AS area_id,array_agg(event_days_arr) as event_days
FROM A 
WHERE
    event_days_arr->>'start_datetime' >= '2020-12-22' AND
    event_days_arr->>'end_datetime' < '2020-12-22T19:00:00'
GROUP BY event_id,event_alias
ORDER BY 
    (event_days_arr->0->>'start_datetime'),(event_days_arr->0->>'end_datetime');

如您所见,我有一个名为“event_days”的数组,我想根据该数组第一个元素中的键对结果进行排序

由于某种原因,我有以下错误

ERROR:  column "a.event_days_arr" must appear in the GROUP BY clause or be used in an aggregate function

我已经尝试过 event_days、event_days_arr,但我无法根据每一行的 JSONB 数组的第一个元素对查询结果进行排序

有人可以帮我整理这个查询的结果吗?

解决方法

将时间范围的WHERE条件移动到公表表达式中,一旦在FROM子句中使用set返回函数(应该使用的地方)就可以完成:

外层聚合也应该使用 jsonb_agg() 而不是 array_agg() 来完成,因为 -> 运算符只为 JSON 数组定义,而不是为原生数组定义。

列别名只能在 ORDER BY 部分“按原样”使用。如果要使用表达式,则需要第二级,例如带有派生表或 CTE:

WITH A AS
(
  SELECT *
  FROM event
    cross join jsonb_array_elements(event_days) as arr(event_day)
  WHERE arr.event_day ->> 'start_datetime' >= '2020-12-22' 
    AND arr.event_day ->> 'end_datetime' < '2020-12-22T19:00:00'
)
SELECT * 
FROM (
  SELECT event_id::int,MAX(area_id)::int AS area_id,jsonb_agg(event_day) as event_days
  FROM A 
  GROUP BY event_id,event_alias
) t
ORDER BY event_days -> 0 ->> 'start_datetime',event_days -> 0 ->>'end_datetime'
,

我认为您想按 event_days 的第一个元素而不是 event_days_arr 的第一个元素进行分组。

这不就行了吗?

WITH A AS (
    SELECT *,jsonb_array_elements(event_days) as event_days_arr
    FROM event
)
SELECT event_id::int,MAX(area_id::int) AS area_id,ARRAY_AGG(event_days_arr) as event_days
FROM A 
WHERE
    event_days_arr ->> 'start_datetime' >= '2020-12-22' AND
    event_days_arr ->> 'end_datetime'   <  '2020-12-22T19:00:00'
GROUP BY event_id,event_alias
ORDER BY 
    event_days -> 0 ->> 'start_datetime',event_days -> 0 ->> 'end_datetime';

Postgres 应该能够识别 order by 列依赖于 event_id。如果没有,那么您需要将列添加到 group by 子句中:

GROUP BY event_id,event_alias,event_days -> 0 ->> 'start_datetime',event_days -> 0 ->> 'end_datetime'