【问题标题】:PostgreSQL ERROR: set-returning functions must appear at top level of FROMPostgreSQL 错误:返回集合的函数必须出现在 FROM 的顶层
【发布时间】:2019-09-24 09:36:19
【问题描述】:

所以我有一个 postgres 数据库,其中有一个名为 jsonb 的字段 details

sensor | details
------------------
A      | [{"direction":"up", "result": 1.0}, {"direction":"up", "result": 2.0}]
B      | [{"direction":"up", "result": 3.0}, {"direction":"down", "result": 4.0}]
B      | [{"direction":"up", "result": 5.0}, {"direction":"up", "result": 6.0}, {"direction":"down", "result": 7.0}]
A      | [{"direction":"down", "result": 8.0}, {"direction":"left", "result": 9.0}]

我现在需要按传感器分组的所有上行记录和下行记录的result 的总和。

所以我的查询应该是这样的:

sensor | up_sum | down_sum
---------------------------
A      | 3.0      | 8.0
B      | 14.0     | 11.0

我需要以某种方式:

  1. 循环遍历 details 数组对象
  2. updown 过滤
  3. 然后将每个传感器的这些记录的结果相加

我猜子查询是这样做的唯一方法(对吗?)。我找到了the postgres documentation on how to handle json,于是开始循环遍历对象:

SELECT jsonb_array_elements(details)
FROM table;

这只是给了我一个对象列表。所以我现在需要按up 过滤,我认为我需要使用json_to_recordset()。我试过这个:

SELECT *
FROM json_to_recordset('[{"direction":"up", "result": 1.0}, {"direction":"up", "result": 2.0}, {"direction":"down", "result": 3.0}]') as x(direction varchar, result float)
WHERE direction = 'up';

结果是预期的

direction | result
------------------
up        | 1
up        | 2

现在让我们总结一下:

SELECT SUM(result) as up_sum
FROM json_to_recordset('[{"direction":"up", "result": 1.0}, {"direction":"up", "result": 2.0}, {"direction":"down", "result": 3.0}]') as x(direction varchar, result float)
WHERE direction = 'up';

太好了,这行得通!

现在我将它插入到我之前的查询中:

SELECT 
    jsonb_array_elements(details),
    (
        SELECT SUM(result)
        FROM json_to_recordset('[{"direction":"up", "result": 1.0}, {"direction":"up", "result": 2.0}, {"direction":"down", "result": 3.0}]') as x(direction varchar, result float)
        WHERE direction = 'up'
    ) as up_sum
FROM table;

好的,这也很好用。

现在我只需要在json_to_recordset() 中使用jsonb_array_elements(details) 的结果(或者实际上jsonb_to_recordset() 用于jsonb 字段)。所以我然后运行了这个:

SELECT 
    jsonb_array_elements(details),
    (
        SELECT SUM(result)
        FROM jsonb_to_recordset(jsonb_array_elements(details)) as x(direction varchar, result float)
        WHERE direction = 'up'
    ) as up_sum
FROM table;

不幸的是,这给出了一个错误:

错误:返回集合的函数必须出现在 FROM 的顶层

谁能提示我正确的方向?

【问题讨论】:

  • 您的“将其插入我以前的”查询不起作用。是不是少了一个逗号? dbfiddle.uk/…
  • @S-Man - 好点。我更正了。

标签: sql postgresql


【解决方案1】:

我不太清楚你的方式。看起来很复杂。

但是:你得到的错误:因为jsonb_array_elements() 不只返回一个,而是返回多个(一组记录,所以,它是一个“集合返回函数”)。一组记录不能直接用作另一个函数的参数。这意味着“在顶层”。这样的函数只能直接作为FROM列表元素出现。


除此之外:这是我为达到您的结果而选择的方式:

demo:db<>fiddle

仅获取 up 和:

SELECT 
    sensor,
    SUM((elems ->> 'result')::numeric) AS up_sum    -- 3
FROM
    mytable,
    jsonb_array_elements(details) elems             -- 1
WHERE elems ->> 'direction' = 'up'                  -- 2
GROUP BY sensor
  1. 将数组元素分别展开为一行
  2. direction 值过滤这些元素
  3. result 值求和

如果您想获得两个方向的总和,您可以使用 FILTER 子句的条件聚合:

SELECT 
    sensor,
    SUM((elems ->> 'result')::numeric)
        FILTER (WHERE elems ->> 'direction' = 'up') AS up_sum,
    SUM((elems ->> 'result')::numeric) 
        FILTER (WHERE elems ->> 'direction' = 'down') AS down_sum
FROM
    mytable,
    jsonb_array_elements(details) elems
GROUP BY sensor

【讨论】:

  • 这是一个很棒的答案。我花了一些时间来理解它,但我现在明白了。还有一个问题,我还想要up 元素的第 50 个百分位数。我为此找到了percentile_disc() 函数。但我不确定如何在elems 上使用它。有什么想法吗?
  • 我不确定你期待什么结果(这有什么帮助吗?dbfiddle.uk/…
【解决方案2】:

试试下面的脚本

select a."sensor",(a."Result"->> 'direction')::character varying as "Direction",sum((a."Result"->> 'result')::float)::integer as "Result" from (
SELECT "sensor",jsonb_array_elements("details"::jsonb) "Result" FROM "A_Test1212"
) a
group by a."sensor",(a."Result"->> 'direction')::character varying 
order by a."sensor"

'A';'up';3
'A';'left';9
'A';'down';8
'B';'up';14
'B';'down';11

-- 透视脚本

select * from crosstab(
'select a."sensor",(a."Result"->> ''direction'')::character varying as "Direction",sum((a."Result"->> ''result'')::float)::integer as "Result" from (
SELECT "sensor",jsonb_array_elements("details"::jsonb) "Result" FROM "A_Test1212"
) a
group by "sensor",(a."Result"->> ''direction'')::character varying
order by a."sensor"') as finalresult("sensor" character varying,"up" integer,"down" integer,"left" integer);


'A';3;9;8
'B';14;11;<NULL>

【讨论】:

    猜你喜欢
    • 2023-02-04
    • 2019-02-22
    • 1970-01-01
    • 2020-11-04
    • 2021-02-10
    • 2019-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多