【问题标题】:postgresql create aggregate with order by and default parameterpostgresql 使用 order by 和默认参数创建聚合
【发布时间】:2017-09-16 13:39:38
【问题描述】:

在 Postgres 9.x 中我可以这样做

select dept_id, string_agg(user_id, ':' order by user_id) 
from dept_user 
group by dept_id;
+---------+------------+
| dept_id | string_agg |
+---------+------------+
| d1      | u1:u2:u3   |
| d2      | u3:u4      |
+---------+------------+

但我的公司使用 Postgres 8.3,所以我发现聚合函数可以像 string_agg 那样做

create schema WMSYS;
create or replace function WMSYS.sf_concat(text,text) returns text as $$
  select case when coalesce($1, '') <> '' then $1||','||$2 else $2 end;
$$ language sql called on null input;
create aggregate WMSYS.wm_concat (text) (sfunc=WMSYS.sf_concat,stype=text);

结果是:

select dept_id, WMSYS.wm_concat(user_id) 
from dept_user
group by dept_id;
+---------+-----------+
| dept_id | wm_concat |
+---------+-----------+
| d1      | u3,u1,u2  |
| d2      | u3,u4     |
+---------+-----------+

但是结果没有排序(u3,u1,u2应该是u1,u2,u3)并且连接字符串(,)不是参数。 我想要这样的用法:

WMSYS.wm_concat(user_id)            ## join by ',' and don't sort
WMSYS.wm_concat(user_id, ':')       ## join by ':' and don't sort
WMSYS.wm_concat(user_id, ':', true) ## join by ':' and order by user_id

怎么做?

【问题讨论】:

标签: sql postgresql aggregate plpgsql


【解决方案1】:

试试这个:

准备数据样本:

t=# create table a1 (i int, t text);
CREATE TABLE
t=# insert into a1 select 1,'u'||g from generate_series(1,9,1) g;
INSERT 0 9
t=# update a1 set i =2 where ctid > '(0,4)';
UPDATE 5
t=# select i,WMSYS.wm_concat(t) from a1 group by i;
 i |   wm_concat
---+----------------
 1 | u1,u2,u3,u4
 2 | u5,u6,u7,u8,u9
(2 rows)

只是添加另一个参数:

create or replace function WMSYS.sf_concat(text,text,text) returns text as $$
  select case when coalesce($1, '') <> '' then $1||$3||$2 else $2 end;
$$ language sql called on null input;

create aggregate WMSYS.wm_concat (text,text) (sfunc=WMSYS.sf_concat,stype=text);

t=# select i,WMSYS.wm_concat(t,':') from a1 group by i;
 i |   wm_concat
---+----------------
 1 | u1:u2:u3:u4
 2 | u5:u6:u7:u8:u9
(2 rows)

现在,我不知道窗口函数在 8.3 中的工作情况如何,并且我没有 env 可以尝试是否可以工作:

t=# select i, max(t) from (select i,WMSYS.wm_concat(t,':') over (partition by i order by t desc) t from a1) a group by i;
 i |      max
---+----------------
 1 | u4:u3:u2:u1
 2 | u9:u8:u7:u6:u5
(2 rows)

但正如 Roman 所建议的,这应该:

t=# select i,WMSYS.wm_concat(t,':') from (select * from a1 order by i asc,t desc) a group by i;
 i |   wm_concat
---+----------------
 1 | u4:u3:u2:u1
 2 | u9:u8:u7:u6:u5
(2 rows)

所以你控制排序不是作为聚合函数的参数,而是你向它呈现数据的方式

【讨论】:

    【解决方案2】:

    请尝试:

    SELECT dept_id, replace(WMSYS.wm_concat(user_id, ',', ':')
      FROM (
            SELECT dept_id, user_id
              FROM dept_user
             ORDER BY 1, 2
           ) AS A
     GROUP BY dept_id;
    

    【讨论】:

    • 这可以做我想做的,但我希望WMSYS.wm_concat有参数来控制连接字符串和排序,那么这个函数将很容易重用。
    • 警告。有了这个解决方案,a,1b,2 的结合是a:1:b:2 而不是a,1:b,2
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 2021-10-30
    相关资源
    最近更新 更多