使用递归CTE的超快DISTINCT:
USE tempdb; GO DROP TABLE dbo.Test; GO CREATE TABLE dbo.Test ( data INTEGER NOT NULL,); GO CREATE CLUSTERED INDEX c ON dbo.Test (data); GO -- Lots of duplicated values INSERT dbo.Test WITH (TABLOCK) (data) SELECT TOP (5000000) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) / 117329 FROM master.sys.columns C1,master.sys.columns C2,master.sys.columns C3; GO SET STATISTICS TIME ON; -- 1591ms cpu SELECT DISTINCT data FROM dbo.Test;
– 15ms cpu
WITH RecursiveCTE AS ( SELECT data = MIN(T.data) FROM dbo.Test T UNION ALL SELECT R.data FROM ( -- A cunning way to use TOP in the recursive part of a CTE Smile SELECT T.data,rn = ROW_NUMBER() OVER (ORDER BY T.data) FROM dbo.Test T JOIN RecursiveCTE R ON R.data < T.data ) R WHERE R.rn = 1 ) SELECT * FROM RecursiveCTE OPTION (MAXRECURSION 0); SET STATISTICS TIME OFF; GO DROP TABLE dbo.Test;
递归CTE是效率的100倍:-)这种加速对我目前的项目来说是非常有价值的,但我不知道在哪种情况下这种方法是有益的.
说实话:我不知道为什么这样加快了查询,为什么数据库不能做这个优化本身.你能解释一下这是如何工作的,为什么它如此有效?
编辑:我看到与sybase类似的效果,所以这种方法似乎不适用于sql-server.
子问题:递归CTE对其他数据库系统有用吗?
解决方法
Why the database cannot do this optimization itself?
Is the recursive CTE useful for other data base systems as well?
优化器并不完美,并没有实现所有可能的技术.人们要求微软实施.请参阅此连接项目Implement Index Skip Scan.它将被关闭,因为不会修复,但这并不意味着它将来不会被解决.其他DBMS可能已经实现了它(连接项表示Oracle实现了这个优化).如果在DBMS引擎中实现了这种优化,则不需要这种“技巧”,优化器将根据可用的统计信息选择最优的计算结果的方法.
I don’t get why this speeds up the query that much.
I am not sure in which cases this approach is beneficial
简单的DISTINCT查询扫描整个索引. “扫描”表示它从磁盘读取索引的每个页面,并聚合内存中的值(或tempdb)以获取不同值的列表.
如果你知道表有很多行,但是只有很少的不同的值,那么阅读所有这些重复值就是浪费时间.递归CTE强制服务器寻找第一个不同值的索引,然后寻找第二个值的索引等等. “Seek”表示服务器在索引中使用二进制搜索来查找该值.通常一个搜索需要从磁盘读取几页. “索引”是一棵平衡的树.
如果表只有几个不同的值,则比查阅索引的所有页面要更加快速寻找几次.另一方面,如果有很多不同的值,那么按顺序读取所有页面比搜索每个连续的值更快.这应该给你一个想法在什么情况下这种方法是有益的.
显然,如果桌子很小,扫描速度会更快.只有当桌子变得“足够大”时,才能看到性能上的差异.
dba.se有一个相关的问题:Is it possible to get seek based parallel plan for distinct/group by?