postgresql PL/PGSQL函数返回动态查询结果,多行

ctrmrzij  于 5个月前  发布在  PostgreSQL
关注(0)|答案(2)|浏览(88)

我在下面的测试表上工作:

select * from test;
 id | broker  | affiliate | leads 
----+---------+-----------+-------
  1 | broker1 | aff1      |     1
  2 | broker1 | aff2      |     2
  3 | broker2 | aff2      |     4
(3 rows)

字符串
我想创建一个函数,它将接受group by列名和where predicate 。下面是该函数的工作示例:

DROP FUNCTION report2(TEXT,TEXT);
CREATE OR REPLACE FUNCTION report2(
    group_by_column_name TEXT,
    sql_where TEXT
)
RETURNS RECORD
AS $$
DECLARE
    query TEXT;
    result_row RECORD;
BEGIN
    query := 'SELECT ';
    IF group_by_column_name <> '' THEN
        query := query || group_by_column_name || ', ';
    END IF;
    query := query || 'sum(leads) FROM test';
    IF sql_where <> '' THEN
        query := query || ' WHERE ' || sql_where;
    END IF;
    IF group_by_column_name <> '' THEN
        query := query || ' GROUP BY(' || group_by_column_name || ')';
    END IF;
    RAISE NOTICE 'query: %;', query;

    

    EXECUTE query INTO result_row;
    RETURN result_row;

END
$$ LANGUAGE 'plpgsql';


它的用法:

select report2('broker', '');
NOTICE:  query: SELECT broker, sum(leads) FROM test GROUP BY(broker);
   report2   
-------------
 (broker1,3)
(1 row)


我不仅要获取查询的第一行,而且要获取所有行(显然)。
我尝试了以下方法:

DROP FUNCTION report2(TEXT,TEXT);
CREATE OR REPLACE FUNCTION report2(
    group_by_column_name TEXT,
    sql_where TEXT
)
RETURNS SETOF RECORD
AS $$
DECLARE
    query TEXT;
    result_row RECORD;
BEGIN
    query := 'SELECT ';
    IF group_by_column_name <> '' THEN
        query := query || group_by_column_name || ', ';
    END IF;
    query := query || 'sum(leads) FROM test';
    IF sql_where <> '' THEN
        query := query || ' WHERE ' || sql_where;
    END IF;
    IF group_by_column_name <> '' THEN
        query := query || ' GROUP BY(' || group_by_column_name || ')';
    END IF;
    RAISE NOTICE 'query: %;', query;

--  Does not worK:
--     FOR result_row IN EXECUTE query
--     LOOP
--         RETURN NEXT result_row;
--     END LOOP;

-- Does not worK:
--     RETURN QUERY EXECUTE query;


END
$$ LANGUAGE 'plpgsql';


在这两种情况下,对于FORRETURN QUERY,都会发生错误:

NOTICE:  query: SELECT broker, sum(leads) FROM test GROUP BY(broker);
ERROR:  materialize mode required, but it is not allowed in this context
CONTEXT:  PL/pgSQL function report2(text,text) line 21 at RETURN NEXT


从[docs][1]:

If multiple rows are returned, only the first will be assigned to the INTO variable(s).


那么,如何才能获得全部结果呢?

[1]: https://(https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN)

j2datikz

j2datikz1#

我会返回setof json而不是record,因为结果行的数量不同,jsonrecord更有用。所以函数如下:

create function report2(arg_group text default '', arg_where text default '')
returns setof json language plpgsql as
$$
begin
  return query execute format(
    'select to_json(t) from (select %s sum(leads) from test %s %s) t', 
    case when arg_group = '' then '' else arg_group || ',' end,
    case when arg_where = '' then '' else 'where ' || arg_where end,
    case when arg_group = '' then '' else 'group by ' || arg_group end
   ); 
end; 
$$

select report2('broker');

字符串
| 报告2|
| --|
| {“broker”:“broker1”,“sum”:3}|
| {“broker”:“broker 2”,“sum”:4}|

wz1wpwve

wz1wpwve2#

你在选择列表中使用了一个返回集合的函数。SRF的返回类型可能是明确指定的或可预测的,但你的记录集返回的怪物不符合条件。
从技术上讲,你提出的函数是完全正确的,精细和可用的-你可以 * 调用它,只是不是你尝试的方式。

select * from report2('broker', '')as(col1 text,col2 bigint);

字符串
| col1| col2|
| --|--|
| 经纪人1| 3 |
| 经纪人2| 4 |
你甚至可以让它把这些备份到记录中:

select row(col1,col2) from report2('broker', '')as(col1 text,col2 bigint);


| 行|
| --|
| (broker 1,3)|
| (经纪人2,4)|
如果使用row()构造函数来防止解包,甚至可以将其构建到函数中:

FOR result_row IN EXECUTE query
     LOOP
         RETURN NEXT row(result_row);
     END LOOP;


然后你可以公开地说它只返回 * 记录 不关你的事 * 类型):

select * from report2('broker', '')as(r record);


| R|
| --|
| (broker 1,3)|
| (经纪人2,4)|
我不知道你想做什么,为什么要这样做,但是多态类型可能会有帮助,如果你不介意 Package 和打开,来回转换和重新转换,把东西打包到jsonbsuggestedStefanov.sm* 可能会更容易。
如果你想json Package 结果,jsonb通过更好的索引提供了更高的性能,由于额外的functions and operators比常规的json更灵活,加上jsonb自动清理无关紧要的空白,排序和重复数据删除你的键,并且更紧凑地存储和传输。

create function report2(group_by_column_name text default null, 
                        sql_where            text default 'true')
returns setof jsonb language plpgsql as $$ begin 
return query execute format(' 
  select to_jsonb(a) from (select %1$s sum(leads) from test where %2$s %3$s)a '
 , group_by_column_name||',' 
 , sql_where 
 , 'group by '||group_by_column_name
); end $$;


默认的where true条件在保存参数检查时会被优化掉。你不必检查group by列是否为null/empty -如果是,||将使逗号和group by文字无效。null||'something'产生null,并将其赋予format()占位符%s不会在那里放置任何东西。
函数runs the same, just in less text

相关问题