SQLite: создавать равные диапазоны дат и запрашивать данные на их основе? - PullRequest
0 голосов
/ 11 июля 2020

У меня есть данные в таблице со схемой:

Id INTEGER,
date DATETIME,
value REAL

id - это первичный ключ, и у меня есть индекс в столбце даты, чтобы ускорить запрос значений в пределах указанного диапазона дат c.

Что мне делать, если мне нужно N равных диапазонов дат между указанными c датами начала и окончания и запрашивать агрегированные данные для каждого диапазона дат?

Например:

  • Дата начала: 01.01.2015
  • Дата окончания: 2019-12-31
  • N: 5

В этом случае должны быть равные интервалы дат :

  • 2015-01-01 ~ 2015-12-31
  • 2016-01-01 ~ 2016-12-31
  • 2017-01-01 ~ 2017-12-31
  • 2018-01-01 ~ 2018-12-31
  • 2019-01-01 ~ 2019-12-31

И запрос должен агрегировать все значения (AVG) между этими интервалами, поэтому я хотел бы иметь 5 строк после выполнения.

Может быть, что-то с CTE?

1 Ответ

1 голос
/ 11 июля 2020

Есть 2 способа сделать это. Оба они используют рекурсивные ctes, но возвращают разные результаты.

1-й с NTILE():

with 
  dates as (select '2015-01-01' mindate, '2019-12-31' maxdate),
  alldates as (
    select mindate date from dates
    union all
    select date(a.date, '1 day') 
    from alldates a cross join dates d
    where a.date < d.maxdate
  ),
  groups as (
    select *, ntile(5) over (order by date) grp
    from alldates
  ),
  cte as (
    select min(date) date1, max(date) date2
    from groups
    group by grp
  )
select * from cte; 

Результаты:

| date1      | date2      |
| ---------- | ---------- |
| 2015-01-01 | 2016-01-01 |
| 2016-01-02 | 2016-12-31 |
| 2017-01-01 | 2017-12-31 |
| 2018-01-01 | 2018-12-31 |
| 2019-01-01 | 2019-12-31 |

И 2-й строит группы с помощью математики:

with 
  dates as (select '2015-01-01' mindate, '2019-12-31' maxdate),
  cte1 as (
    select mindate date from dates
    union all
    select date(
      c.date, 
      ((strftime('%s', d.maxdate) - strftime('%s', d.mindate)) / 5) || ' second'
    )
    from cte1 c inner join dates d
    on c.date < d.maxdate
  ),
  cte2 as (
    select date date1, lead(date) over (order by date) date2
    from cte1
  ),
  cte as (
    select date1, 
      case 
        when date2 = (select maxdate from dates) then date2
        else date(date2, '-1 day')
      end date2
    from cte2
    where date2 is not null
  )  
select * from cte

Результаты:

| date1      | date2      |
| ---------- | ---------- |
| 2015-01-01 | 2015-12-31 |
| 2016-01-01 | 2016-12-30 |
| 2016-12-31 | 2017-12-30 |
| 2017-12-31 | 2018-12-30 |
| 2018-12-31 | 2019-12-31 |

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

select c.date1, c.date2, avg(t.value) avg_value 
from cte c inner join tablename t
on t.date between c.date1 and c.date2
group by c.date1, c.date2
...