PostgresQL window 在连续 ID 块上运行

PostgresQL window function over blocks of continuous IDs

我有一个 table 具有部分连续的整数 ID,即有 1,2,3, 6,7,8, 10, 23,24,25,26.

这样的块

我对从 table 中选择的 简单 解决方案感到头疼 并包含一列,其中的值对应于相应块的第一个 id。

即像这样

select id, first(id) over <what goes here?> first from table;

结果应该如下所示

| id | first |
|----|-------|
| 1  | 1     |
| 2  | 1     |
| 3  | 1     |
| 6  | 6     |
| 7  | 6     |
| 8  | 6     |
| 10 | 10    |
| 23 | 23    |
| 24 | 23    |
| 25 | 23    |
| 26 | 23    |

之后,我可以将此专栏与 partition by window 函数子句很好地结合使用。

到目前为止我想出的总是看起来与此相似但没有成功:

WITH foo AS (
    SELECT LAG(id) OVER (ORDER BY id)  AS previous_id,
           id                          AS id,
           id - LAG(id, 1, id) OVER (ORDER BY id) AS first_in_sequence
    FROM table)
SELECT *,
       FIRST_VALUE(id) OVER (ORDER BY id) AS first
FROM foo
ORDER BY id;

定义自定义 postgres 函数也是一个可接受的table解决方案。

感谢任何建议,

马蒂

这是一个如何做到这一点的想法。隐式游标虽然效率不高。

create or replace function ff()
returns table (r_id integer, r_first integer)
language plpgsql as
$$
declare
  running_previous integer;
  running_id integer;
  running_first integer := null;
begin 
    for running_id in select id from _table order by id loop
        if running_previous is distinct from running_id - 1 then
            running_first := running_id;
        end if;
        r_id := running_id;
        r_first := running_first;
        running_previous := running_id;
        return next;
    end loop;
end
$$;
-- test
select * from ff() as t(id, first);

在 Postgres 中,您可以 create a custom aggregate. 示例:

create or replace function first_in_series_func(int[], int)
returns int[] language sql immutable
as $$ 
    select case 
        when [2] is distinct from - 1 then array[, ]
        else array[[1], ] end; 
$$;

create or replace function first_in_series_final(int[])
returns int language sql immutable
as $$
    select [1]
$$;

create aggregate first_in_series(int) (
    sfunc = first_in_series_func,
    finalfunc = first_in_series_final,
    stype = int[]
);

Db<>fiddle.

阅读文档:User-Defined Aggregates