计算列中不同值的最快方法,包括NULL值

时间:2011-09-11 05:52:56

标签: asp.net sql sql-server database tsql

Transact-Sql Count Distinct操作计算列中的所有非空值。我需要计算一组表中每列的不同值的数量,包括空值(因此,如果列中有空值,则结果应为(Select Count(Distinct COLNAME) From TABLE) + 1

这将在DB中每个表的每一列上重复。包括数百个表,其中一些表有超过1M行。因为这需要在每一列上完成,所以为每列添加索引不是一个好的选择。

这将作为ASP.net站点的一部分完成,因此与代码逻辑的集成也可以(即:这不必作为一个查询的一部分完成,但如果可以以良好的性能完成,然后更好)。

最有效的方法是什么?


测试后更新

我从一个好的代表性表格上给出的答案中测试了不同的方法。该表有320万条记录,数十列(少数有索引,大多数没有)。一列有320万个唯一值。其他列的范围从所有Null(一个值)到最多40K唯一值。对于每种方法,我进行了四次测试(每次尝试多次,平均结果):一次20列,一次5列,1列有很多值(3.2M),1列有少量值( 167)。以下是最快到最慢的结果

  1. Count/GroupByCheran
  2. CountDistinct+SubQueryEllis
  3. dense_rankEriksson
  4. Count+MaxAndriy
  5. 测试结果(以秒为单位):

       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
    • 的表现竞争

    感谢您的帮助和建议!

6 个答案:

答案 0 :(得分:9)

SELECT COUNT(*)
FROM (SELECT ColumnName
      FROM TableName
      GROUP BY ColumnName) AS s;

GROUP BY选择包含NULL的不同值。 COUNT(*)将包含NULL,而COUNT(ColumnName)则忽略NULL。

答案 1 :(得分:7)

我认为您应该尝试保持表扫描的数量并一次计算一个表中的所有列。这样的事情值得尝试。

;with C as
(
  select dense_rank() over(order by Col1) as dnCol1,
         dense_rank() over(order by Col2) as dnCol2
  from YourTable
)
select max(dnCol1) as CountCol1,
       max(dnCol2) as CountCol2
from C       

SE-Data

测试查询

答案 2 :(得分:3)

开发OP自己的解决方案:

SELECT
  COUNT(DISTINCT acolumn) + MAX(CASE WHEN acolumn IS NULL THEN 1 ELSE 0 END)
FROM atable

答案 3 :(得分:2)

运行一个查询计算Distinct值的数量,如果列中有任何NULL,则添加1(使用子查询)

Select Count(Distinct COLUMNNAME) +
       Case When Exists 
                 (Select * from TABLENAME Where COLUMNNAME is Null) 
            Then 1 Else 0 End
From TABLENAME

答案 4 :(得分:2)

您可以尝试:

count(
distinct coalesce(
    your_table.column_1, your_table.column_2
    -- cast them if you want replace value from column are not same type
    )
) as COUNT_TEST

函数coalesce可帮助您将两列组合为replace not null值。

我在我的情况下使用了这个,并且正确地得到了成功。

答案 5 :(得分:0)

不确定这是最快的,但可能值得测试。用例赋予null一个值。显然,您需要选择一个不会在实际数据中出现的null值。根据查询计划,这将是Cheran S提出的计数(*)(分组)解决方案的死热。

    SELECT 
        COUNT( distinct
                 (case when [testNull] is null then 'dbNullValue' else [testNull] end)
             )
    FROM [test].[dbo].[testNullVal]

使用此方法也可以计算多个列

    SELECT 
        COUNT( distinct
                 (case when [testNull1] is null then 'dbNullValue' else [testNull1] end)
             ),
        COUNT( distinct
                 (case when [testNull2] is null then 'dbNullValue' else [testNull2] end)
             )
    FROM [test].[dbo].[testNullVal]