Dav*_*veB 12 postgresql window-functions gaps-and-islands postgresql-9.0
我正在尝试在 PostgreSQL 9.0 中构建一个查询,该查询获取特定列的最长连续行序列。
考虑下表:
lap_id (serial), lap_no (int), car_type (enum), race_id (int FK)
Run Code Online (Sandbox Code Playgroud)
lap_no
每个(race_id, car_type)
. where都是独一无二的。
我希望查询为给定的race_id
and生成最长的序列car_type
,因此它将返回int
最高的(或长的)。
使用以下数据:
1, 1, red, 1
2, 2, red, 1
3, 3, red, 1
4, 4, red, 1
5, 1, blue, 1
6, 5, red, 1
7, 2, blue, 1
8, 1, green, 1
Run Code Online (Sandbox Code Playgroud)
对于car_type = red and race_id = 1
查询将5
作为lap_no
字段的最长序列返回。
(我也想知道car_type
所有种族给定的最长序列,但我计划自己解决这个问题。)
Erw*_*ter 20
您的描述会生成如下表定义:
CREATE TABLE tbl (
lap_id serial PRIMARY KEY
, lap_no int NOT NULL
, car_type enum NOT NULL
, race_id int NOT NULL -- REFERENCES ...
, UNIQUE(race_id, car_type, lap_no)
);
Run Code Online (Sandbox Code Playgroud)
要获得最长的序列(1 个结果,最长的,如果有平局,则任意选择):
SELECT race_id, car_type, count(*) AS seq_len
FROM (
SELECT *, count(*) FILTER (WHERE step)
OVER (ORDER BY race_id, car_type, lap_no) AS grp
FROM (
SELECT *, (lag(lap_no) OVER (PARTITION BY race_id, car_type ORDER BY lap_no) + 1)
IS DISTINCT FROM lap_no AS step
FROM tbl
) x
) y
GROUP BY race_id, car_type, grp
ORDER BY seq_len DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
count(*) FILTER (WHERE step)
only counts TRUE
(= step to next group),这会为每个新组产生一个新数字。
关于 SO 的相关问题,一个答案是使用 plpgsql的程序解决方案:
如果最高要求是性能,则plpgsql 函数在这种特殊情况下通常更快,因为它可以在单次扫描中计算结果。
我们可以利用连续 lap_no
定义序列的事实,得到一个更简单和更快的版本:
SELECT race_id, car_type, count(*) AS seq_len
FROM (
SELECT race_id, car_type
, row_number() OVER (PARTITION BY race_id, car_type ORDER BY lap_no) - lap_no AS grp
FROM tbl
) x
GROUP BY race_id, car_type, grp
ORDER BY seq_len DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
连续的圈数以相同的方式结束grp
。每丢失一圈都会导致grp
每个分区降低。
这取决于(race_id, car_type, lap_no)
存在UNIQUE NOT NULL
。NULL 值或重复项可能会破坏逻辑。
@杰克的版本有效计算所有的圈(行)在先前lap_no
在此race_id
具有相同的car_type
。这是简单,快速和正确的-只要每次car_type
只能有一个每个序列race_id
。
但是对于这么简单的任务,查询可能会更简单。从逻辑上讲,所有lap_no
per(car_type, race_id)
必须按顺序排列,我们可以只计算圈数:
SELECT race_id, car_type, count(*) AS seq_len
FROM tbl
GROUP BY race_id, car_type
ORDER BY seq_len DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
另一方面,如果每个race_idcar_type
可以有多个单独的序列(并且问题没有另外指定),Jack 的版本将失败。
回复问题中的评论/澄清:将查询限制为给定 的查询(race_id, car_type)
会使其更快,当然:
SELECT count(*) AS seq_len
FROM (
SELECT row_number() OVER (ORDER BY lap_no) - lap_no AS grp
FROM tbl
WHERE race_id = 1
AND car_type = 'red'
) x
GROUP BY grp
ORDER BY seq_len DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
最佳性能的关键是拟合指数(除了上述使用单个顺序扫描的程序解决方案)。像这样的多列索引效果最好:
CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);
Run Code Online (Sandbox Code Playgroud)
如果您的表有UNIQUE
约束我认为在顶部,也就是只有这一点(唯一)的索引内部实现,你也不会需要创建另一个索引。
Run Code Online (Sandbox Code Playgroud)create table tbl (lap_no int, car_type text, race_id int);
Run Code Online (Sandbox Code Playgroud)create table tbl (lap_no int, car_type text, race_id int);
Run Code Online (Sandbox Code Playgroud)insert into tbl values (1,'red',1),(2,'red',1),(3,'red',1),(4,'red',1), (1,'blue',1),(5,'red',1),(2,'blue',1),(1,'green',1);
Run Code Online (Sandbox Code Playgroud)select car_type, race_id, sum(case when lap_no=(prev+1) then 1 else 0 end)+1 seq_len from ( select *, lag(lap_no) over (partition by car_type, race_id order by lap_no) prev from tbl ) z group by car_type, race_id order by seq_len desc limit 1;
归档时间: |
|
查看次数: |
9110 次 |
最近记录: |