Самый быстрый способ подсчета отдельных значений в столбце, включая значения NULL - PullRequest
10 голосов
/ 11 сентября 2011

Операция Transact-Sql Count Distinct подсчитывает все ненулевые значения в столбце. Мне нужно посчитать количество различных значений на столбец в наборе таблиц, включая нулевые значения (поэтому, если в столбце есть нулевое значение, результат должен быть (Select Count(Distinct COLNAME) From TABLE) + 1.

Это будет повторяться для каждого столбца в каждой таблице в БД. Включает в себя сотни таблиц, некоторые из которых имеют более 1 млн строк. Поскольку это необходимо делать для каждого отдельного столбца, добавление индексов для каждого столбца не является хорошим вариантом.

Это будет сделано как часть сайта ASP.net, так что интеграция с логикой кода также в порядке (то есть: это не нужно выполнять как часть одного запроса, хотя, если это можно сделать с хорошей производительностью тогда еще лучше).

Какой самый эффективный способ сделать это?

<ч />

Обновление после тестирования

Я проверил различные методы из ответов, приведенных в хорошей репрезентативной таблице. Таблица содержит 3,2 миллиона записей, десятки столбцов (несколько с индексами, большинство без). Один столбец имеет 3,2 миллиона уникальных значений. Другие столбцы варьируются от всех Null (одно значение) до максимум 40K уникальных значений. Для каждого метода я выполнил четыре теста (с несколькими попытками на каждом, усредняя результаты): 20 столбцов за один раз, 5 столбцов за один раз, 1 столбец с множеством значений (3.2M) и 1 столбец с небольшим количеством значений ( 167). Вот результаты, в порядке от самого быстрого до самого медленного

  1. Count / GroupBy ( Cheran )
  2. CountDistinct + SubQuery ( Ellis )
  3. density_rank ( Eriksson )
  4. Количество + Макс ( Андрей )

Результаты тестирования (в секундах):

   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, запрос включал подзапрос Таким образом, на каждый столбец было отправлено два запроса. Возможно, потому что выигрыш, который был бы достигнут за счет ограничения количества сканирований таблиц, невелик по сравнению с ударом по производительности с точки зрения требований к памяти (только предположение).
  • Хотя метод dens_rank, безусловно, самый элегантный, кажется, что он плохо масштабируется (см. Результат для 20 столбцов, что является худшим из четырех методов), и даже в небольшом масштабе просто не может конкурировать с производительностью Count.

Спасибо за помощь и предложения!

Ответы [ 6 ]

9 голосов
/ 11 сентября 2011
SELECT COUNT(*)
FROM (SELECT ColumnName
      FROM TableName
      GROUP BY ColumnName) AS s;

GROUP BY выбирает различные значения, включая NULL. COUNT(*) будет включать NULL, в отличие от COUNT(ColumnName), который игнорирует NULL.

7 голосов
/ 11 сентября 2011

Я думаю, вы должны попытаться уменьшить количество сканирований таблицы и сосчитать все столбцы в одной таблице за один раз.Возможно, стоит попробовать что-то подобное.

;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

3 голосов
/ 11 сентября 2011

Разработка собственного решения ОП:

SELECT
  COUNT(DISTINCT acolumn) + MAX(CASE WHEN acolumn IS NULL THEN 1 ELSE 0 END)
FROM atable
2 голосов
/ 22 сентября 2011

Вы можете попробовать:

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

Функция объединения поможет вам объединить два столбца с заменой ненулевых значений.

Я использовал это в моем случае и успешно с правильным результатом.

2 голосов
/ 11 сентября 2011

Выполнить один запрос, который считает количество различных значений и добавляет 1, если в столбце есть какие-либо значения NULL (с ​​использованием подзапроса)

Select Count(Distinct COLUMNNAME) +
       Case When Exists 
                 (Select * from TABLENAME Where COLUMNNAME is Null) 
            Then 1 Else 0 End
From TABLENAME
0 голосов
/ 11 сентября 2011

Не уверен, что это будет самый быстрый, но, возможно, стоит попробовать. Случай использования, чтобы дать нулевое значение. Ясно, что вам нужно будет выбрать значение для нуля, которое не встречается в реальных данных. В соответствии с планом запроса это будет мертвая жара с решением по количеству (*) (group by), предложенным 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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...