Postgresql中的自定义序列或窗口函数

时间:2016-09-20 21:41:18

标签: postgresql grouping window-functions

我有一张巨大的桌子,看起来像这样

Group | Code | Value
------|------|------
G1    |0     |V1
G1    |2     |V2
G1    |2     |V3     
G1    |1     |V4
G1    |2     |V5
G1    |2     |V6

G2    |0     |V7
G2    |1     |V8
G2    |1     |V9     
G2    |2     |V10
G2    |2     |V11
G2    |2     |V12
G2    |2     |V13

我需要生成一个像这样的表

Group | Code | Value
------|------|------
G1    |0     |V1
G1    |2     |V2+V3
G1    |1     |V4
G1    |2     |V5+V6

G2    |0     |V7
G2    |1     |V8
G2    |1     |V9     
G2    |2     |V10+V11
G2    |2     |V12+V13

code = 2始终显示为一对,我需要总结两个连续的code = 2值。有关如何使用SQL语句而不是存储过程的任何想法?任何可以帮助我做到这一点的特殊功能。我正在尝试Postgres 9.6谢谢。

2 个答案:

答案 0 :(得分:1)

我假设您希望按Value列的数字部分对行进行排序。如果我们在表格中调用t,那么这是一个使用窗口函数执行所需操作的查询:

SELECT "group", code, string_agg(value, '+')
FROM
   (SELECT *, (row_number() OVER (PARTITION BY "group", code ORDER BY n) - 1) / CASE code WHEN 2 THEN 2 ELSE 1 END AS code_group
    FROM (SELECT *,substr(value,2)::integer AS n FROM t) t1
   ) t2
GROUP BY "group", code, code_group
ORDER BY min(n);

我们的想法是首先提取(在子查询中)value的数字部分,以便我们以后可以将其用作排序键。然后我们使用以下复杂的表达:

(row_number() OVER (PARTITION BY "group", code ORDER BY n) - 1) / CASE code WHEN 2 THEN 2 ELSE 1 END

这基本上分配给每组行(按groupcode分组)从0开始的递增数字。因此第一行为0,下一行为1,依此类推。但是有一个例外。如果code是2,那么我们使用以下编号方案:0,0,1,1,2,2 ......这是通过将行号除以2来实现的。我称这个数字为code_group。在最后一步中,我们按groupcode(与之前一样)进行分组,但也code_group分组,以便将带有code=2的连续行对合并为一对。

答案 1 :(得分:1)

select *
from (
    select 
        grp, code, 
        case when code = 2 then
            case when rn & 1 = 1 then null
            else concat(lag(value) over w, '+', value)
            end
        else value
        end as value
    from (
        select *, row_number() over w rn
        from a_table
        window w as (partition by code order by grp, code)
        ) s
    window w as (order by grp, code, rn)
    ) s
where value notnull
order by grp, code

步骤:

  • 按组和代码在分区上添加行号
    • 如果code = 2且其行号为odd,则选择null作为值
    • 如果code = 2且其行号为偶数,则将其值和之前的值连接为值
    • 如果代码<> 2然后只选择值
  • 跳过具有空值的行。