【发布时间】:2011-11-14 15:34:44
【问题描述】:
Transact-Sql Count Distinct 操作对列中的所有非空值进行计数。我需要计算一组表中每列的不同值的数量,包括空值(所以如果列中有空值,结果应该是(Select Count(Distinct COLNAME) From TABLE) + 1。
这将在数据库中每个表的每一列中重复。包括数百个表,其中一些表超过 1M 行。因为这需要在每一列上完成,所以为每一列添加索引不是一个好的选择。
这将作为 ASP.net 站点的一部分完成,因此与代码逻辑的集成也可以(即:这不必作为一个查询的一部分完成,但如果可以以良好的性能完成的话,然后更好)。
最有效的方法是什么?
测试后更新
我根据一张具有代表性的表格给出的答案测试了不同的方法。该表有 320 万条记录,几十列(少数有索引,大多数没有)。一列有 320 万个唯一值。其他列的范围从所有 Null(一个值)到最多 40K 个唯一值。对于每种方法,我进行了四次测试(每次尝试多次,平均结果):一次 20 列,一次 5 列,1 列有很多值(3.2M)和 1 列有少量值( 167)。以下是结果,按从快到慢的顺序排列
测试结果(以秒为单位):
Method 20_Columns 5_Columns 1_Column (Large) 1_Column (Small)
1) Count/GroupBy 10.8 4.8 2.8 0.14
2) CountDistinct 12.4 4.8 3 0.7
3) dense_rank 226 30 6 4.33
4) Count+Max 98.5 44 16 12.5
注意事项:
- 有趣的是,最快的两种方法(到目前为止,两者之间只有很小的差异)都是为每列提交单独查询的方法(在结果 #2 的情况下,查询包括一个子查询,因此每列实际上提交了两个查询)。可能是因为与内存需求方面的性能损失相比,通过限制表扫描次数获得的收益很小(只是猜测)。
- 虽然dense_rank方法绝对是最优雅的,但它似乎并不能很好地扩展(见20列的结果,这是迄今为止四种方法中最差的),即使是小规模也不能与
Count的表现一较高下。
感谢您的帮助和建议!
【问题讨论】:
-
这是一个非常令人沮丧的问题。
COUNT(DISTINCT ...)几乎完成了所需的操作,甚至通知您它已经丢弃了NULL和Warning: Null value is eliminated by an aggregate or other SET operation.但这只会在 Cheran 的答案同样有效的单列查询中使用。即使列不是NULL-able,但有时COUNT(DISTINCT ...)是最好的,有时是DENSERANK版本。然而,这两种计划形状似乎都很固定。 -
如果您对具有
n列的表中的所有列执行此操作,则dense_rank版本将有效地复制整个表并以n方式对副本进行排序。我希望最好的用例是在执行(非索引)列的子集时,其中未包含的列存储在数据页中并且非常宽。
标签: asp.net sql sql-server database tsql