2011-06-11 83 views
6

我正在开发一个查询,以针对包含时间序列中一堆点的表进行查询。该表可以增长得相当大,所以我希望查询通过在固定的时间间隔内平均点来有效地对输出进行下采样。在编写查询之后,我对SQL Server(2008)如何选择执行查询感到惊讶。执行计划揭示了一种不必要的排序操作,随着时间序列的增长,这种操作将变得非常昂贵这是问题所在,降低到一个简单的例子:集合分组单调函数的冗余分类

CREATE TABLE [dbo].[Example] 
(
    [x] FLOAT NOT NULL, 
    [y] FLOAT NOT NULL, 
    PRIMARY KEY CLUSTERED 
    (
     [x] ASC 
    ) 
); 

SELECT FLOOR([x]), AVG([y]) 
FROM [dbo].[Example] 
GROUP BY FLOOR([x]); 

在这里,我(X,Y),它们已经用x排序(因为聚集主键的)对,并且我对每个平均ÿ整数x(通过截断FLOOR函数)。我希望表格已经适当地排序,因为FLOOR是一个单调函数。不幸的是,SQL Server会需要这些数据重新排序,这里是执行计划:

Example Execution Plan

不宜SQL服务器能够在一个单调功能分组数据进行流聚集已经适当排序的列?

是否有一种通用的方法来重写这样的查询,以便SQL Server能够看到命令被保留下来?

[更新] 我发现关于这个问题Things SQL needs: sargability of monotonic functions的文章,正如标题所暗示的,好像这是一个优化的SQL Server目前还不做(在大多数情况下)。

这里有超过[dbo].[Example]甚至更​​简单的查询证明了一点:

SELECT [x], [y] 
FROM [dbo].[Example] 
ORDER BY FLOOR([x]) --sort performed in execution plan 

SELECT [x], [y] 
FROM [dbo].[Example] 
ORDER BY 2*[x] --NO sort performed in execution plan 

SELECT [x], [y] 
FROM [dbo].[Example] 
ORDER BY 2*[x]+1 --sort performed in execution plan 

在任何单一的加法或乘法,查询优化了解到,该数据已经有相同的顺序(这是见过你当组也通过这样的表达)。所以看起来优化器可以理解单调函数的概念,但通常不会被应用。

我现在正在测试计算列/索引解决方案,但似乎这样会大大增加持久数据的大小,因为我需要几个索引来覆盖可能的时间间隔范围。

回答

3

一些注意事项:

  • 时看到桌子是空的计划,并在表有X行可以是完全不同的计划
  • 我不认为这是正确的有主键的计划在X领域。可以有两个具有相同X值的点吗?

我想,如果你这样做,你将有最好的查询性能:

create table Point 
(
    PointId int identity(1, 1) 
     constraint PK_Example_Id primary key, 
    X float not null, 
    Y float not null, 
    FloorX as floor(x) persisted 
) 

create index IX_Point_FloorX_Y on Point(FloorX, Y) 

添加一些行:

declare @RowCount int = 10000 
while(@RowCount > 0) 
begin 
    insert Point 
    values (cast(crypt_gen_random(2) as int), cast(crypt_gen_random(2) as int)) 
    set @RowCount -= 1 
end 

查询:

select floor(X), avg(Y) 
from Point 
group by floor(X) 

select FloorX, avg(Y) 
from Point 
group by FloorX 

都将有同样的计划

计划:不排序

enter image description here

另一种选择 - 你可以创建索引视图。在这种情况下,您必须直接查询视图,除非您有企业版,即使您直接查询表,该企业版也会使用索引视图索引。

[编辑]刚刚意识到我没有明确回答你的问题。你问为什么SQL将执行排序如果X是群集主键。 SQL不对X执行排序,它对floor(x)执行排序。换句话说,如果x已经排序,那么f(x)不一定具有相同的顺序,对吧?

+0

感谢您的建议亚历克斯。在我实际使用的数据中,域是时间,标准的'datetime'数据类型的分辨率足以满足唯一性(抽样速率比3.33ms更快)。此外,用于对结果进行分组的功能并不总是相同的。在这个例子中,它是“floor”,但是对于实时系列数据,它可以是每30秒,每5分钟,每10天等。我不确定在每个可能的时间间隔内坚持和维护计算列和索引是否切合实际。 – 2011-06-12 00:02:21

+0

@Michael Petito - 重读你的问题,并意识到我没有明确回答你的问题。更新了答案。另外,如果不为您分组的值添加索引,我认为您无法实现良好的性能。 – 2011-06-12 00:08:11

+0

为了回答你的编辑,根据定义,像floor这样的单调(增加)函数满足条件:如果x <= y,那么f(x)<= f(y)'(保存顺序)。所以是的,我的问题的目的是,由于x已经排序,所以f(x)已经排序,因此对于f(x)分组的任何聚合都不需要排序。 – 2011-06-12 00:09:46

1

这是一个非常好的问题。在这种情况下,我们希望有另一个表并使用CROSS APPLY,如下面的示例所示,它使用表Numbers将Min(X)/ YourStepInMinutes和Max(x)/ YourStepInMinutes之间的所有数字以及Min和最大。该查询运行作为嵌套循环,不需要排序:

SELECT n.n, Avg(p.y) 
FROM dbo.Numbers AS n 
CROSS APPLY (SELECT p.y 
    FROM dbo.Points AS p 
    WHERE p.x<n*YourStepInMinutes AND (n-1)*YourStepInMinutes<=p.x 
) As p 

编辑:虽然这种解决方案也需要一个连接这是不是免费的,我不会让毯子语句中总是慢。对大量数据进行排序可能会突然变得非常缓慢 - 您将排序的行数增加了10%,排序可能会慢10倍。另一方面,这种方法可以更好地扩展,因为它不需要很大的排序。

此外,因为我们不需要一个持久的计算列,我们可以立即使用此查询的任何大小的间隔,如17分钟。

+0

这是一个有趣的方法 - 由于连接,它可能比Alex Aza的解决方案慢,但可能需要更少的存储空间,因为可以重新使用计算的间隔。 – 2011-06-12 16:27:43

2

当索引列上有任何函数时,SQL Server几乎总是忽略索引。有充分的理由:

  • 查询优化器(QO)使用数据分布统计。
    该功能改变了这个:你是否期望统计数据生成每个查询
  • 功能(在这种情况下,绝对)可在指数
    的QO可以用它独特的无效独特的计划生成

一些优化技术进行编码,以QO(例如:COUNT VS在EXISTS一个IF)但它没有做严格的数学证明:它们不适用于查询响应时间

对于某些日期时间函数,也有MS Connect(我实际上不同意这个函数,因为函数的排列太多优化出来:所以我们会有不一致)

否则,从亚历克斯氮杂索引的计算列的解决方案是什么,我会做

编辑:

看了你在更新的问题的链接。

FLOOR改变严格单调到单调。也就是说,x是唯一的,所以严格是单调的。 FLOOR(x)是单调的。

如果您有任何WHERE子句,则统计数据变得重要:正如您所说的,您发布了简化示例。

对于您发布的x * 2 + 1示例:您认为SQL Server 应该在什么时候停止评估表达式?这是一个基于成本的优化器当然..

我认为这是公平的,SQL Server的行为是这样的:日常我的EXISTS优化示例更有用。

+0

我不希望每个查询都会生成统计信息,但同时我不明白为什么统计信息对确定数据是否适当排序很重要。是的,该功能使唯一性无效,但不是秩序,我不认为这需要很多努力才能证明(请参阅我的更新中的文章)。这肯定会影响查询响应时间;排序是一个相对昂贵的操作! – 2011-06-12 16:18:55

+0

@Michael Petito:查看我的更新请 – gbn 2011-06-12 16:33:04

+0

+1您的MS Connect链接实际上也是我想要做的。太糟糕了,微软的回应没有链接到对“众多解决方法”的检查。 – 2011-06-12 16:34:38