连续序列的分区函数

时间:2015-08-20 20:24:42

标签: sql postgresql postgresql-9.3 window-functions

有一个以下结构的表格:

CREATE TABLE history
(
  pk serial NOT NULL,
  "from" integer NOT NULL,
  "to" integer NOT NULL,
  entity_key text NOT NULL,
  data text NOT NULL,
  CONSTRAINT history_pkey PRIMARY KEY (pk)
);

pk是主键,fromto定义序列中的位置以及由entity_key标识的给定实体的序列本身。因此,如果第一行具有from = 1; to = 2而第二行具有from = 2; to = 3,则实体具有一行2行。所以重点是前一行的to与下一行的from匹配。

确定" next" /"之前"的顺序行由pk定义,单调增长(因为它是SERIAL)。

序列不必以1开头,to - from不必始终为1。所以它可以是from = 1; to = 10。重要的是" next"序列中的行与to完全匹配。

示例数据集:

pk  |  from  |  to  |  entity_key  |  data
----+--------+------+--------------+-------
1   |   1    |   2  |      42      |  foo
2   |   2    |   3  |      42      |  bar
3   |   3    |   4  |      42      |  baz
4   |  10    |  11  |      42      |  another foo
5   |  11    |  12  |      42      |  another baz
6   |   1    |   2  |     111      |  one one one
7   |   2    |   3  |     111      |  one one one two
8   |   3    |   4  |     111      |  one one one three

而我无法实现的是如何按"序列"这里我可以将窗口函数应用于代表单个"序列"。

的组

我想说我想使用row_number()函数,并希望得到以下结果:

pk  |  row_number | entity_key
----+-------------+------------
1   |     1       |       42
2   |     2       |       42
3   |     3       |       42
4   |     1       |       42
5   |     2       |       42
6   |     1       |      111
7   |     2       |      111
8   |     3       |      111

为方便起见,我创建了一个带有初始种子的SQLFiddle:http://sqlfiddle.com/#!15/e7c1c

PS:它不是"给我代码"问题,我做了自己的研究,我只是想出如何分区。

很明显我需要LEFT JOIN使用next.from = curr.to,但之后仍然不清楚如何重置next.from IS NULL上的分区。

PS:对于提供所需结果的最优雅查询,它将获得100分赏金

PPS:由于某些其他限制超出了本问题的范围,所需的解决方案应该是SQL查询而不是pgsql。

3 个答案:

答案 0 :(得分:7)

我不知道它是否算“优雅”,但我认为这会做你想做的事情:

with Lagged as (
  select
    pk,
    case when lag("to",1) over (order by pk) is distinct from "from" then 1 else 0 end as starts,
    entity_key
  from history
), LaggedGroups as (
  select
    pk,
    sum(starts) over (order by pk) as groups,
    entity_key
  from Lagged
)
  select
    pk,
    row_number() over (
      partition by groups
      order by pk
    ) as "row_number",
    entity_key
from LaggedGroups

答案 1 :(得分:2)

您可以使用generate_series()生成两个值之间的所有行。然后你可以使用行号的差异:

select pk, "from", "to",
       row_number() over (partition by entity_key, min(grp) order by pk) as row_number
from (select h.*,
             (row_number() over (partition by entity_key order by ind) -
              ind) as grp
      from (select h.*, generate_series("from", "to" - 1) as ind
            from history h
           ) h
     ) h
 group by pk, "from", "to", entity_key

因为您指定差异在1到10之间,所以实际上可能没有这么糟糕的表现。

不幸的是,你的SQL Fiddle现在无法正常工作,所以我无法测试它。

答案 2 :(得分:0)

那么, 这不完全是一个 SQL查询,但是:

select a.pk as PK, a.entity_key as ENTITY_KEY, b.pk as BPK, 0 as Seq into #tmp 
from history a left join history b on a."to" = b."from" and a.pk = b.pk-1 

declare @seq int

select @seq = 1

update #tmp set Seq =  case when (BPK is null) then @seq-1 else @seq end,  
@seq = case when (BPK is null) then @seq+1 else @seq end

select pk, entity_key, ROW_NUMBER() over (PARTITION  by entity_key, seq order by pk asc) 
from #tmp order by pk

这是在SQL Server 2008中