如何解决聚合分组单调函数的冗余排序
| 我正在针对包含时间序列中许多点的表开发查询。该表可能会变得很大,因此我希望查询通过对固定时间间隔内的点进行平均来有效地对输出进行下采样。编写查询后,我对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
函数截断)。由于expect1ѭ是单调函数,我希望该表已针对聚集进行适当排序。不幸的是,SQL Server决定需要对这些数据进行重新排序,这是执行计划:
SQL Server是否应该能够对按已适当排序的列的单调函数分组的数据执行流式聚合?
是否存在重写此类查询的通用方法,以便SQL Server可以看到顺序已保留?
[更新]
我找到了一篇有关SQL需要的主题的文章:单调函数的可维护性,正如标题所示,这似乎是SQL Server尚未进行的优化(在大多数情况下)。
以下是通过ѭ3进行的更简单的查询,证明了这一点:
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
两者将有相同的计划
计划:无排序
另一个选择-您可以创建索引视图。在这种情况下,您将必须直接查询视图,除非您拥有Enterprise Edition,即使直接查询表也将使用索引视图索引。
[编辑]刚刚意识到我没有明确回答您的问题。您问如果ѭ9是集群主键,为什么SQL会执行排序。 SQL不对ѭ9进行排序,而是对ѭ11进行排序。换句话说,如果已经对x
进行了排序,那么f(x)
不一定具有相同的顺序,对吗?
, 当索引列上有任何功能时,SQL Server几乎总是忽略索引。有充分的理由:
查询优化器(QO)使用数据分布的统计信息。
该函数改变了这一点:您希望每个查询生成统计信息吗?
该函数(在这种情况下,肯定是)可以使索引的唯一性无效
QO可以在其计划生成中使用唯一性
一些优化已被编码到QO中(例如:IF中的COUNT vs EXISTS),但它没有严格的数学证明:它们不适用于查询响应时间
也有一些日期时间函数的MS Connect(我实际上不同意,因为有太多的函数排列无法优化:所以我们会有不一致的地方)
否则,我会做Alex Aza的索引计算列解决方案
编辑:
阅读更新问题中的链接。
地板严格从单调变为单调。也就是说,x是唯一的,因此严格单调。 FLOOR(x)是单调的。
如果您有WHERE子句,统计信息就变得很重要:正如您所说的,您发布了简化的示例。
对于您发布的x * 2 +1示例:您认为SQL Server应该在什么时候停止对表达式求值?当然,这是基于成本的优化器。
我认为SQL Server的行为是这样的:每天我的EXISTS优化示例都非常有用。
,这个问题问得好。在这种情况下,我们希望有另一个表并使用CROSS APPLY,例如以下示例,该表使用表Numbers存储所有在Min(X)/ YourStepInMinutes和Max(x)/ YourStepInMinutes之间的数字,以及在Min和X周围另外两个数字最高该查询作为嵌套循环运行,不需要排序:
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分钟)使用此查询。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。