我正在针对包含时间序列中的一堆点的表开发一个查询。该表可能会变得非常大,因此我希望查询通过对固定时间间隔内的点进行平均来有效地对输出进行下采样。编写查询后,我对 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 的 y 进行平均(通过使用 FLOOR
进行截断) > 功能)。我希望该表已经针对聚合进行了适当排序,因为 FLOOR
是一个单调函数。不幸的是,SQL Server 决定需要对这些数据重新排序,执行计划如下:
SQL Server 不应该能够对按已适当排序的列的单调函数分组的数据执行流式聚合吗?
有没有通用的方法来重写此类查询,以便 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
在任何单个加法或乘法中,查询优化器都知道数据已经具有相同的顺序(当您按此类表达式进行分组时也会看到这一点)。因此,优化器似乎理解单调函数的概念,但并未普遍应用。
我现在正在测试计算列/索引解决方案,但这似乎会显着增加持久数据的大小,因为我需要多个索引来覆盖可能的间隔范围。
最佳答案
一些注意事项:
- 当表为空时您看到的计划和表有 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
两者都有相同的计划
计划:无排序
另一个选项 - 您可以创建索引 View 。在这种情况下,您将必须直接查询 View ,除非您有企业版,即使您直接查询表,企业版也会使用索引 View 索引。
[编辑]刚刚意识到我没有明确回答您的问题。您问如果X
是聚集主键,SQL为什么要执行排序。 SQL 不对X
执行排序,而是对floor(x)
执行排序。换句话说,如果 x
已经排序,那么 f(x)
不一定具有相同的顺序,对吧?
关于sql-server - 按单调函数分组的聚合的冗余排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6319020/