Нахождение длины серии в postgres - PullRequest
0 голосов
/ 15 мая 2018

Хитрый запрос для postgres. Представьте себе, у меня есть набор строк с логическим столбцом, который называется (например) успех. Как это:

id | success  
9  | false  
8  | false  
7  | true  
6  | true  
5  | true  
4  | false  
3  | false  
2  | true  
1  | false  

И мне нужно рассчитать длину последней (не) удачной серии. Например в этом случае это будет «3» для успешного и «2» для не успешного. Или используя оконные функции, затем что-то вроде:

id | success | length  
9  | false   | 2  
8  | false   | 2  
7  | true    | 3  
6  | true    | 3  
5  | true    | 3  
4  | false   | 1  
3  | true    | 2  
2  | true    | 2  
1  | false   | 1  

(обратите внимание, что мне, как правило, нужна длина только последних серий, а не всех)

Самый близкий ответ, который я нашел, был этой статьей: https://jaxenter.com/10-sql-tricks-that-you-didnt-think-were-possible-125934.html (См. № 5)

Однако postgres не поддерживает опцию «IGNORE NULLS», поэтому запрос не работает. Без «IGNORE NULLS» он просто возвращает мне нули в столбце длины.

Вот самое близкое, что мне удалось получить:

WITH
  trx1(id, success, rn) AS (
    SELECT id, success, row_number() OVER (ORDER BY id desc) 
    FROM results
  ),
  trx2(id, success, rn, lo, hi) AS (
    SELECT trx1.*,
    CASE WHEN coalesce(lag(success) OVER (ORDER BY id DESC), FALSE) != success THEN rn END,
    CASE WHEN coalesce(lead(success) OVER (ORDER BY id DESC), FALSE) != success THEN rn END
    FROM trx1
  )
SELECT trx2.*, 1
- last_value (lo) IGNORE nulls OVER (ORDER BY id DESC ROWS BETWEEN
UNBOUNDED PRECEDING AND CURRENT ROW)
+ first_value(hi) OVER (ORDER BY id DESC ROWS BETWEEN CURRENT ROW
AND UNBOUNDED FOLLOWING)
AS length FROM trx2;

Есть ли у вас идеи такого запроса?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

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

with last_success as (
  select max(id) id from my_table where success
)
select count(mt.id) last_fails_count
from my_table mt, last_success lt
where mt.id > lt.id;

--------------------
| last_fails_count |
--------------------
| 2                |
--------------------

DbFiddle

Это вдвое быстрее, если мне нужно только получить последний провальный или успешный сериал.

0 голосов
/ 15 мая 2018

Вы можете использовать оконную функцию row_number() для обозначения серии:

select max(id) as max_id, success, count(*) as length
from (
    select *, row_number() over wa - row_number() over wp as grp
    from my_table
    window
        wp as (partition by success order by id desc),
        wa as (order by id desc)
    ) s
group by success, grp
order by 1 desc

 max_id | success | length 
--------+---------+--------
      9 | f       |      2
      7 | t       |      3
      4 | f       |      2
      2 | t       |      1
      1 | f       |      1
(5 rows)

DbFiddle.

...