使用DISTINCT COUNT的性能

时间:2012-12-18 00:45:37

标签: sql sql-server performance tsql indexing

我正在运行SQL Server 2012。

我有一个查询,当它条纹化时,它的最基本形式如下:

SELECT COUNT(DISTINCT fullAddress) as quickCount 
FROM leads
WHERE yearID >=12 AND yearID <=21

潜在客户表中有大约1.49亿条记录。 leadID上有一个聚簇索引,一个非聚集索引是YearID上的索引,并且包含fullAddress。

这个查询大约需要40秒才能运行。我意识到这并不错,但在这种情况下还不够快。

我查看了执行计划,据我所知,60%的费用是DISTINCT COUNT。

当我在没有DISTINCT COUNT的情况下运行相同的查询时:

SELECT COUNT(*) as quickCount 
FROM leads
WHERE yearID >=12 AND yearID <=21

运行只需1秒。

不幸的是,我需要计算不同的完整地址。所以我想弄清楚我能做些什么来让第一个查询运行得更快。

以下是两个查询的执行计划的屏幕截图:

enter image description here

以下链接指向更大的内容 - http://www.sequenzia.com/execPlan.jpg

据我所知,我的主要问题是Distinct Sort(52%)。

对此的任何帮助或反馈都会很棒。

谢谢!

更新

我接受了Thilo的建议并应用了这个索引:

 CREATE INDEX IDX_X ON LEADS(FULLADDRESS, YEARID);

我实际上创建了两个新的测试表,每个测试表中都有相同的1百万条记录。我将同样的原始索引应用于两者,然后将上述索引应用于一个。现在,当我比较同一执行计划中的2个表时,具有上述指数的表略好于48%到52%。以下是新的执行计划 - http://www.sequenzia.com/execPlan2.jpg

这有些帮助,但我真的需要更多的表现。还有其他想法吗?

4 个答案:

答案 0 :(得分:3)

要尝试的一件事是通过在fullAddress上排序索引来排除排序(也包括yearID列,以便您也可以满足where子句。)< / p>

CREATE INDEX IDX_X ON LEADS(FULLADDRESS, YEARID);

这样,你应该得到一个快速完整索引扫描(可能仍然比你的非独特计数的索引范围扫描慢,但希望比你的40s排序更快)。

但为什么它需要如此之快?这不是你需要做的事情,对吧?如果这是针对公共网站的,我认为你可以通过稍微过时的缓存结果来逃避。

答案 1 :(得分:0)

如果您不经常更新此表,则可以查看使用正在运行的查询创建索引视图 http://msdn.microsoft.com/en-us/library/aa933148(v=sql.80).aspx

答案 2 :(得分:0)

此答案无助于解决原始问题(SQL Server 2012)。它将在Azure SQL DB和最可能在内部版本的SQL Server的新版本中运行。

  

Approximate Count Distinct enters Public Preview in Azure SQL Database

     

近似查询处理是一个新的功能家族,旨在提供跨非常大的数据集的聚合,在这些数据集中,响应性比绝对精度更为关键。 ...

     

新的APPROX_COUNT_DISTINCT聚合函数返回一组中唯一非空值的大概数量。

     

优势

     

与详尽无误的精确COUNT DISTINCT相比,APPROX_COUNT_DISTINCT使用更少的内存来计算不重复计数。因此,即使数据集中有数十亿行,也很可能可以在内存中完成计算而不会溢出到磁盘。如果COUNT DISTINCT的内存不足并将数据溢出到TempDB,这会导致很大的性能损失,则APPROX_COUNT_DISTINCT通常不会溢出。

     

部分是由于未将大型数据集溢出到TempDB,部分是由于内部存在算法,对于非常大的数据集,APPROX_COUNT_DISTINCT的执行速度将比COUNT DISTINCT快得多。

     

更多{@ {3}}

SELECT APPROX_COUNT_DISTINCT(fullAddress) as quickCount 
FROM leads
WHERE yearID >=12 AND yearID <=21

答案 3 :(得分:0)

我会尝试使用子查询进行过滤,然后再进行不同的计数

WITH cte as (SELECT ID
FROM leads
WHERE yearID >=12 AND yearID <=21)
SELECT COUNT(DISTINCT fullAddress) as quickCount 
FROM leads
JOIN cte on cte.ID = leads.ID

这并不总是在所有情况下都执行得更快,但据我所知,当您在没有执行不同操作的列上使用 where 子句时,速度会更快。