【问题标题】:SQLite: create equal date ranges and query data based on them?SQLite:创建相等的日期范围并根据它们查询数据?
【发布时间】:2020-07-11 07:07:45
【问题描述】:

我在带有架构的表中有数据:

Id INTEGER,
date DATETIME,
value REAL

id 是主键,我在日期列上有一个索引来加快查询特定日期范围内的值。

如果我需要在特定的开始日期和结束日期之间有 N 个相等的日期范围,并查询每个日期范围的聚合数据,我该怎么办?

例如:

  • 开始日期:2015-01-01
  • 结束日期: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 有关?

【问题讨论】:

  • 你的earlier question about this没有可用的答案吗?
  • 很遗憾没有。因为让我们假设我从 2019-01-01 到 2019-12-31 有 200 个数据。 1 月份的数据收集更频繁,所以我在 1 月份有 100 行,其余 100 行。将创建大小为 20 的存储桶 (N=10)。因此,如果您在图表上将其可视化,则 X 轴上的日期会失真。

标签: sqlite common-table-expression


【解决方案1】:

有两种方法可以做到这一点。 它们都使用递归 ctes,但返回不同的结果。

第一个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 |

第二个用数学构建组:

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

【讨论】:

  • 谢谢。看起来很合适。我做了类似的事情,但无法获得低于 500ms 的性能。我将日期更改为整数,这大大减少了执行时间,但仍然在 6-700 毫秒左右。基表有约 700k 行。日期有索引。你认为 700k 行应该在 6-700ms 内完成吗? i7 桌面,大量 RAM,最新 SQLite,使用 O3 构建。
  • SQLite 不是最适合这些情况的 rdbms。尽管 700k 行数据量并不大,但您不能指望得到更好的结果。您可以通过尝试不同的方法来进行实验。例如,我首先发布的第一个查询构造了一个 5x365 行的结果集,以便将其分成 5 个组,而第二个查询使用数学运算来创建必须更高性能的组。但最后两个查询都必须连接到 700k 行的表。还有一件事:尝试代替 BETWEEN 使用:on t.date &gt;= c.date1 and t.date &lt;= c.date2
猜你喜欢
  • 1970-01-01
  • 2014-03-13
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 2015-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多