选择最长的连续序列


12

我正在尝试在PostgreSQL 9.0中构造一个查询,以获取特定列的最长连续行序列。

请考虑下表:

lap_id (serial), lap_no (int), car_type (enum), race_id (int FK)

lap_no每个地方都是独特的(race_id, car_type)

我希望查询为给定的race_id和生成最长的序列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

对于car_type = red and race_id = 1查询,将5作为lap_no字段的最长序列返回。

我在这里找到了类似的问题但是我的情况要简单一些。

(我也想知道car_type所有比赛的最长顺序,但我正计划自己解决。)

Answers:


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)
);

此类问题的一般解决方案

要获得最长的序列(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;

count(*) FILTER (WHERE step)仅计数TRUE(=移至下一组),这将为每个新组产生一个新数字。

关于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;

连续的圈数相同grp。每错过一圈,grp每个分区的成绩都会降低。

这取决于(race_id, car_type, lap_no)存在UNIQUE NOT NULL。NULL值或重复项可能会破坏逻辑。

讨论Jack更简单的选择

@杰克的版本有效计算所有的圈(行)在先前lap_no在此race_id具有相同的car_type。只要每个序列car_type只能有一个序列,这将更加简单,快捷和正确race_id

但是对于一个简单的任务,查询可能会更简单。从逻辑上讲,每个lap_no人都(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;

另一方面,如果每个race_idcar_type可以有多个单独的序列(并且问题没有另外说明),则杰克的版本将失败。

对于给定的种族/汽车类型,速度更快

在回答问题中的评论/说明时:将查询限制为给定 的查询(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;

db <> 在这里拨弄
旧的SQL 拨弄

指数

达到最佳性能的关键是合适的索引(所提到的与单个顺序扫描一起使用的过程解决方案除外)。像这样的多列索引最适合:

CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);

如果您的表有UNIQUE约束我认为在顶部,也就是只有这一点(唯一)的索引内部实现,你也不会需要创建另一个索引。


嗨,欧文(Erwin),谢谢您所做的工作,但是在我的数据库上花费了〜17秒!不要以为您可以提供一个修改,使它以race_id和car_type作为参数,而不是比较整个表?(我尝试过重新编写它,并不断
遇到

7

create table tbl (lap_no int, car_type text, race_id int);
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);
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;
/*
|car_type|race_id|seq_len|
|:-------|------:|------:|
|red     |      1|      5|
*/

也许,sum((lap_no=(prev+1))::integer)+1但我不确定这更容易阅读
杰克说请尝试topanswers.xyz 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.