가장 긴 연속 시퀀스 선택


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모든 종족 에게 주어진 가장 긴 순서를 알고 싶지만 스스로 해결할 계획이었습니다.)

답변:


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_noper 당 이 순서대로(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_id여러 개의 개별 시퀀스를car_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;

db <> fiddle here
이전 SQL 바이올린

인덱스

최고 성능의 핵심은 적합 지수입니다 (단일 순차 스캔으로 작업하는 언급 된 절차 솔루션 제외). 이와 같은 다중 열 인덱스가 가장 적합합니다.

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

테이블 UNIQUE에 맨 위에 가정 한 제약 조건 이있는 경우 내부적 으로이 (고유 한) 인덱스로 구현되며 다른 인덱스를 만들 필요 가 없습니다 .


안녕하세요 Erwin, 그 일을 해주셔서 감사하지만 내 데이터베이스에서 ~ 17 초가 걸립니다! 전체 테이블을 비교하는 대신 race_id 및 car_type을 매개 변수로 사용하도록 수정을 제공 할 수 있다고 가정하지 않습니까? (나는 그것을 다시 작성하고 오류가 계속 발생했습니다)
DaveB

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읽기 쉽지 않은지 확실하지 않습니다
Jack은 topanswers.xyz를 시도
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.