2009-08-22 184 views
2

我有数据样本的表格,有时间戳和一些数据。每个表在时间戳上都有一个聚集索引,然后是一个特定于数据的密钥。数据样本不一定是等距的。避免在SQL Server GROUP BY中进行不必要的排序?

我需要在特定的时间范围内对数据进行缩减采样以绘制图表 - 例如,从100,000行到N,其中N大约是50.虽然我可能不得不妥协于算法的“正确性”从DSP的角度来看,我想保留在SQL中出于性能原因。

我目前的想法是将时间范围内的样本分组到N个框中,然后取每组的平均值。在SQL中实现此目的的一种方法是将分区函数应用于范围从0到N-1(含)的日期,然后是GROUP BY和AVG。

我认为这个GROUP BY可以在没有排序的情况下执行,因为日期来自聚簇索引,分区函数是单调的。但是,SQL Server似乎没有注意到这一点,它发出的代码占执行代价的78%(在下面的例子中)。假设我是对的,这种类型是不必要的,我可以使查询速度提高5倍。

有没有办法强制SQL Server跳过排序?还是有更好的方法来解决这个问题?

干杯。 本

IF EXISTS(SELECT name FROM sysobjects WHERE name = N'test') DROP TABLE test 

CREATE TABLE test 
(
    date DATETIME NOT NULL, 
    v FLOAT NOT NULL, 
    CONSTRAINT PK_test PRIMARY KEY CLUSTERED (date ASC, v ASC) 
) 

INSERT INTO test (date, v) VALUES ('2009-08-22 14:06:00.000', 1) 
INSERT INTO test (date, v) VALUES ('2009-08-22 17:09:00.000', 8) 
INSERT INTO test (date, v) VALUES ('2009-08-24 00:00:00.000', 2) 
INSERT INTO test (date, v) VALUES ('2009-08-24 03:00:00.000', 9) 
INSERT INTO test (date, v) VALUES ('2009-08-24 14:06:00.000', 7) 

-- the lower bound is set to the table min for demo purposes; in reality 
-- it could be any date 
declare @min float 
set @min = cast((select min(date) from test) as float) 

-- similarly for max 
declare @max float 
set @max = cast((select max(date) from test) as float) 

-- the number of results to return (assuming enough data is available) 
declare @count int 
set @count = 3 

-- precompute scale factor 
declare @scale float 
set @scale = (@count - 1)/(@max - @min) 
select @scale 

-- this scales the dates from 0 to n-1 
select (cast(date as float) - @min) * @scale, v from test 

-- this rounds the scaled dates to the nearest partition, 
-- groups by the partition, and then averages values in each partition 
select round((cast(date as float) - @min) * @scale, 0), avg(v) from test 
group by round((cast(date as float) - @min) * @scale, 0) 

回答

2

SQL Server确实没有办法知道date集群密钥可以用于像round(cast.. as float))这样的表达式以保证顺序。只有这样,才会抛弃赛道。加入(... [email protected]) * @scale,你自己搞得一团糟。如果您需要对这些表达式进行排序和分组,请将它们存储在保留的计算列中并由它们索引。尽管您可能想要使用DATEPART,但由于经历了不精确的类型(例如float),可能会使表达式无法用于保留的计算列。

更新

datefloat等同的话题:

declare @f float, @d datetime; 
select @d = cast(1 as datetime); 
select @f = cast(1 as float); 
select cast(@d as varbinary(8)), cast(@f as varbinary(8)), @d, cast(@d as float) 

产生以下:

0x0000000100000000 0x3FF0000000000000 1900-01-02 00:00:00.000 1 

所以你可以看到,altough它们都存储在8字节(至少float(25...53)),0123的内部表示不是float,其中整数部分是白天,小数部分是时间(正如通常假定的那样)。

再举一个例子:

declare @d datetime; 
select @d = '1900-01-02 12:00 PM'; 
select cast(@d as varbinary(8)), cast(@d as float) 

0x0000000100C5C100 1.5 

再次浇铸@dfloat的结果是1.5,但0x0000000100C5C100日期时间内部表示将是IEEE双值2.1284E-314,不1.5

+0

在这个例子中,应该是很容易分析至少(... - @ min)* @scale部分。不幸的是,将“日期”列存储为浮点数似乎没有什么区别。 但是,最终你说得对:期待SQL Server自动解决这个问题有点乐观。我真正希望的是一种告诉它假设数据已经被排序的方法。 :) 关于FLOAT不精确,我认为DATETIME只是一个FLOAT内部? – 2009-08-22 19:03:25

+0

查看我的日期和浮动'内部'假设的更新。 – 2009-08-22 20:20:08

+0

啊,这很有趣!谢谢。 – 2009-08-22 21:04:38

1

是,SQL-Server一直有一些问题,这种时间分配summary选择的。分析服务有多种方式来处理它,但数据服务方面则更加有限。

我建议你尝试(我不能尝试或测试任何东西从这里)是做一个次要的“分区表”,其中包含yor分区定义,然后加入反对它。你将需要一些mathcing索引让他有机会工作:

0

有两个问题:

此查询需要多长时间?

你确定它是排序日期吗?另外在计划中它是在排序日期?它分区后?这将是我的猜测。我怀疑它就像它做的第一件事情......也许是它划分或组合它需要再做一次排序的方式。

不管怎么说,即使它的排序已排序列表中,也不会认为,这将需要很长时间,因为它是alredy整理...