有没有更好的方法来创建动态频率上的动态时间平均序列?

chr*_*ean 7 sql-server query t-sql

我有一系列表格,其中包含从各种设备收集的大量高精度数据。收集它们的时间间隔各不相同,甚至在时间序列中徘徊。我的用户希望能够选择日期范围并以特定频率获得这些变量的平均值/最小值/最大值。这是我对此进行的第二次尝试,它有效,但我想知道是否有更好/更快的方法来实现这一目标?

declare @start datetime
declare @end datetime
set @start = '3/1/2012'
set @end = '3/3/2012'
declare @interval int
set @interval = 300

declare @tpart table(
dt datetime
);

with CTE_TimeTable
as
(
select @start as [date]
union all
select dateadd(ss,@interval, [date])
from CTE_TimeTable
where DateAdd(ss,@interval, [date]) <= @end
)
insert into @tpart
select [date] from CTE_TimeTable
OPTION (MAXRECURSION 0);


select t.dt, avg(c.x1), min(c.x1), max(c.x2), avg(c.x2), min(c.x2), max(c.x2)  from clean.data c ,
@tpart t 
where
ABS(DateDIFF(ss, t.dt , c.Date) ) <= @interval /2
and
Date >= @start 
and 
Date <= @end
group by t.dt
Run Code Online (Sandbox Code Playgroud)

现在,这 3 天期间超过 32721 行,此查询大约需要 43 秒才能运行,并为我提供了我期望的 577 行,但我希望更快。大命中来自嵌套循环来执行内部连接。

Mik*_*son 4

表之间的连接嵌入在一个函数中,这使得优化器很难用它来做任何聪明的事情。我想它必须将一个表中的每一行与另一个表中的每一其他行进行比较。

使用范围检查重写连接应该会快很多。我还向您的表变量添加了一个主键,以从查询计划中删除排序操作,并将您的表变量转换为临时表。我的测试的不同之处在于查询计划开始使用并行性。

declare @start datetime;
declare @end datetime;
set @start = '20120301';
set @end = '20120303';
declare @interval int;
set @interval = 300;

create table #tpart
(
  dt datetime primary key
);

with CTE_TimeTable
as
(
  select @start as [date]
  union all
  select dateadd(second ,@interval, [date])
  from CTE_TimeTable
  where dateadd(second, @interval, [date]) <= @end
)
insert into #tpart
select [date]
from CTE_TimeTable
option (maxrecursion 0);

select t.dt, avg(c.x1), min(c.x1), max(c.x2), avg(c.x2), min(c.x2), max(c.x2)
from clean.data c
  inner join #tpart t 
    on c.Date >= t.dt and
       c.Date < dateadd(second, @interval, t.dt)
group by t.dt;

drop table #tpart;
Run Code Online (Sandbox Code Playgroud)

注意:此查询不会返回与您的查询完全相同的间隔。它将日期范围划分为大小相等的部分,其中您的查询在开始处有一半间隔,在范围结束处有一半间隔。如果需要,当然可以修改查询以使其与您的查询等效。

更新

我在一个表上进行了测试,该表的总行数103680134560间隔2012-03-012012-03-03。在我的测试中,原始查询需要 4.1 秒。上面的查询需要0.1秒。

生成测试数据的脚本:

create table clean.data
(
    Date datetime primary key,
    x1 int,
    x2 int
);

go

with C as
(
  select cast('20120201' as datetime) as D
  union all
  select dateadd(second, 5, D)
  from C
  where D < '20120401'
)
insert into clean.data(Date, x1, x2)
select D, checksum(newid()) % 1000, checksum(newid()) % 1000
from C
option (maxrecursion 0);
Run Code Online (Sandbox Code Playgroud)