Sql 连续序列的划分函数
有以下结构的表格:Sql 连续序列的划分函数,sql,postgresql,postgresql-9.3,window-functions,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是一个主键,from和to定义序列中的一个位置以及序列本身,该序列由实体\键标识。因此,
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
是一个主键,from
和to
定义序列中的一个位置以及序列本身,该序列由实体\键
标识。因此,如果第一行的from=1,则实体有一个2行的序列;to=2
,第二个具有from=2;to=3
。因此,这里的要点是前一行的to
与下一行的from
相匹配
确定“下一行”/“上一行”的顺序由单调增长的pk
定义(因为它是一个SERIAL
)
序列不必从1开始,并且并非总是需要从1开始。因此它可以是from=1;to=10
。重要的是序列中的“下一行”与到
完全匹配
样本数据集:
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\u 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
为了方便起见,我创建了一个带有初始种子的SQLFIDLE:
PS:这不是“给我密码”的问题,我做了自己的研究,我只是不知道如何划分
很明显,我需要使用next.from=curr.to
左连接,但是仍然不清楚如何重置next.from上的分区为空
PS:对于提供请求结果的最优雅的查询,将获得100分的奖励
PPS:理想的解决方案应该是SQL查询,而不是pgsql,因为其他一些限制超出了这个问题的范围。我不知道它是否算“优雅”,但我认为这可以满足您的要求:
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
只是为了好玩和完整:一个递归解决方案,重建(双重)记录链表。[这将不是最快的解决方案]
注意:我注释掉了升序pk条件,因为连接逻辑不需要它们
WITH RECURSIVE zzz AS (
SELECT h0.pk
, h0."to" AS next
, h0.entity_key AS ek
, 1::integer AS rnk
FROM history h0
WHERE NOT EXISTS (
SELECT * FROM history nx
WHERE nx.entity_key = h0.entity_key
AND nx."to" = h0."from"
-- AND nx.pk > h0.pk
)
UNION ALL
SELECT h1.pk
, h1."to" AS next
, h1.entity_key AS ek
, 1+zzz.rnk AS rnk
FROM zzz
JOIN history h1
ON h1.entity_key = zzz.ek
AND h1."from" = zzz.next
-- AND h1.pk > zzz.pk
)
SELECT * FROM zzz
ORDER BY ek,pk
;
您可以使用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现在无法工作,因此我无法测试它。好吧,
这不完全是一个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中是的,我称之为“幼稚”解决方案,因为它所做的正是人们在纸上对给定数据集所做的。谢谢。@Gordon Linoff:“序列不必以1开头,而且to-from也不一定总是需要1。所以它可以是from=1;to=10。”是的,现在答案是:-)