Получить число дней подряд, если значение поля устарело - PullRequest
1 голос
/ 27 февраля 2020

Я хотел получить к последовательному количеству записей, что определенное значение поля устарело на основе таблицы ставок. Из приведенных ниже записей данных 3,4,5 имеют ту же скорость, что и 0,770827, поэтому число дней, в течение которых ставка устарела, равно 3, а предыдущий показатель до устаревания - 0,770886. Я хотел бы получить помощь при написании запроса к количеству записей. которые имеют устаревший курс и также достигают предыдущего уровня того же самого. В следующем примере я показываю только CAD к USD, но нам нужно то же самое в разных валютах. Любая помощь будет очень полезна.

enter image description here

Ожидаемый результат

enter image description here

Ответы [ 3 ]

2 голосов
/ 27 февраля 2020

Когда значение меняет строку на 1, в противном случае - 0. Затем суммируйте этот столбец (flg), теперь у вас есть последовательные группы (grp). Используйте grp для агрегирования, подсчета, отображения минимальных и максимальных дат:

dbfiddle demo

select to_cur, from_cur, min(dt) dt_from, max(dt) dt_to, rate, count(1) cnt
  from (
    select dt, to_cur, from_cur, rate, 
           sum(flg) over (partition by to_cur, from_cur order by dt) grp
      from (
        select dt, to_cur, from_cur, rate, 
               case lag(rate) over (partition by to_cur, from_cur order by dt) 
                    when rate then 0 else 1 end flg
          from t))
  group by grp, to_cur, from_cur, rate
  order by from_cur, to_cur, min(dt)

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

  having count(1) >= 3
2 голосов
/ 27 февраля 2020

Это проблема пробелов и островков.

Вы можете использовать lag() для извлечения предыдущего rate для того же кортежа валют, а затем выполнить суммирование окна, чтобы определить группы последовательных записей с помощью та же скорость. Затем вы можете объединить группы и восстановить предыдущий показатель, снова используя lag(). Последний шаг - фильтрация по группам, имеющим не менее 3 записей.

select *
from (
    select
        from_cur,
        to_cur,
        rate,
        max(date) max_date,
        lag(rate) over(partition by from_cur, to_cur order by max(date)) lag_rate_grp,
        count(*) cnt
    from (
        select
            t.*,
            sum(case when rate = lag_rate then 0 else 1 end) over(partition by from_date, to_date order by date) grp 
        from (
            select 
                t.*,
                lag(rate) over(partition by from_cur, to_cur order by date) lag_rate
            from mytable t
        ) t
    ) t
    group by from_cur, to_cur, rate, grp
) t
where cnt >= 3
order by from_cur, to_cur, max_date

На самом деле, использование разницы между номерами строк может сохранить один уровень вложенности:

select *
from (
    select
        from_cur,
        to_cur,
        rate,
        max(date) max_date,
        lag(rate) over(partition by from_cur, to_cur order by max(date)) lag_rate_grp,
        count(*) cnt
    from (
        select
            t.*,
            row_number() over(partition by from_cur, to_cur order by date) rn1,
            row_number() over(partition by from_cur, to_cur, rate order by date) rn2         
        from mytable t
    ) t
    group by from_cur, to_cur, rate, rn1 - rn2
) t
where cnt >= 3
order by from_cur, to_cur, max_date

Если вы хотите, чтобы в кортеже валюты была только самая ранняя запись, вы можете использовать row_number():

select *
from (
    select
        from_cur,
        to_cur,
        rate,
        max(date) max_date,
        lag(rate) over(partition by from_cur, to_cur order by max(date)) lag_rate_grp,
        count(*) cnt,
        row_number() over(partition by from_cur, to_cur, case when count(*) >= 3 then 0 else 1 end order by max(date)) rn
    from (
        select
            t.*,
            row_number() over(partition by from_cur, to_cur order by date) rn1,
            row_number() over(partition by from_cur, to_cur, rate order by date) rn2         
        from mytable t
    ) t
    group by from_cur, to_cur, rate, rn1 - rn2
) t
where cnt >= 3 and rn = 1
order by from_cur, to_cur
0 голосов
/ 27 февраля 2020

Это проблема пробелов и островков, но я бы решил ее, просто вычтя последовательность из даты. А затем агрегирование:

select to_cur, from_cur, rate, min(date), max(date),
       count(*) as days_stale
from (select r.*,
             row_number() over (partition by to_cur, from_cur, rate order by date) as seqnum
      from rates r
     ) r
group by (date - seqnum * interval '1' day)
...