2013-12-07 2 views
0

У меня есть таблица, которая содержит некоторые записи, упорядоченные по дате.PostgreSQL - последующие строки GROUP

И я хочу получить начальную и конечную даты для каждой последующей группы (сгруппированные по некоторым критериям, например, положением).

Example:

create table tbl (id int, date timestamp without time zone, 
        position int); 

insert into tbl values 
(1 , '2013-12-01', 1), 
(2 , '2013-12-02', 2), 
(3 , '2013-12-03', 2), 
(4 , '2013-12-04', 2), 
(5 , '2013-12-05', 3), 
(6 , '2013-12-06', 3), 
(7 , '2013-12-07', 2), 
(8 , '2013-12-08', 2) 

Конечно, если бы я просто группа по положению я получить неверный результат, как позиции могут быть одинаковыми для разных групп:

SELECT POSITION, min(date) MIN, max(date) MAX 
FROM tbl GROUP BY POSITION 

я получаю:

POSITION MIN        MAX 
1   December, 01 2013 00:00:00+0000 December, 01 2013 00:00:00+0000 
3   December, 05 2013 00:00:00+0000 December, 06 2013 00:00:00+0000 
2   December, 02 2013 00:00:00+0000 December, 08 2013 00:00:00+0000 

Но я хочу:

POSITION MIN        MAX 
1   December, 01 2013 00:00:00+0000 December, 01 2013 00:00:00+0000 
2   December, 02 2013 00:00:00+0000 December, 04 2013 00:00:00+0000 
3   December, 05 2013 00:00:00+0000 December, 06 2013 00:00:00+0000 
2   December, 07 2013 00:00:00+0000 December, 08 2013 00:00:00+0000 

Я нашел solution for MySql, который использует переменные, и я мог бы его переносить, но я считаю, что PostgreSQL может сделать это с помощью более продвинутых функций, таких как оконные функции.

Я использую PostgreSQL 9.2

ответ

1

Существует, вероятно, более элегантное решение, но попробуйте это:

WITH tmp_tbl AS (
SELECT *, 
CASE WHEN lag(position,1) OVER(ORDER BY id)=position 
    THEN position 
    ELSE ROW_NUMBER() OVER(ORDER BY id) 
    END AS grouping_col 
FROM tbl 
) 
, tmp_tbl2 AS(
SELECT position,date, 
CASE WHEN lag(position,1)OVER(ORDER BY id)=position 
    THEN lag(grouping_col,1) OVER(ORDER BY id) 
    ELSE ROW_NUMBER() OVER(ORDER BY id) 
    END AS grouping_col 
FROM tmp_tbl 
) 
SELECT POSITION, min(date) MIN, max(date) MAX 
FROM tmp_tbl2 GROUP BY grouping_col,position 
1

Есть некоторые полные ответы на Stackoverflow для этого, поэтому я не буду повторять их в деталях, но принцип его состоит в группе записи в соответствии с разницей между:

  • номер строки при заказе по дате (через оконную функцию)
  • разница между датами и статической датой ссылки.

Так у вас есть ряд таких, как:

rownum datediff diff 
1  1  0^
2  2  0 | first group 
3  3  0 v 
4  5  1^
5  6  1 | second group 
6  7  1 v 
7  9  2^
8  10  2 v third group 
Смежные вопросы