Подсчет DISTINCT по нескольким столбцам

Есть ли лучший способ сделать такой запрос:

SELECT COUNT(*) 
FROM (SELECT DISTINCT DocumentId, DocumentSessionId
      FROM DocumentOutputItems) AS internalQuery

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

Мой запрос работает нормально, но мне было интересно, могу ли я получить окончательный результат, используя только один запрос (без использования подзапроса)

Ответ 1

Если вы пытаетесь повысить производительность, вы можете попытаться создать постоянный вычисленный столбец либо с хэшем, либо с конкатенированным значением двух столбцов.

Как только он будет сохранен, если столбец детерминирован, и вы используете "нормальные" настройки базы данных, его можно индексировать и/или создать статистику на нем.

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

Ответ 2

Изменить: изменено из менее надежного запроса только для контрольной суммы Я нашел способ сделать это (в SQL Server 2005), который очень хорошо работает для меня, и я могу использовать столько столбцов, сколько мне нужно (добавив их в функцию CHECKSUM()). Функция REVERSE() превращает ints в varchars, чтобы сделать четкие более надежные

SELECT COUNT(DISTINCT (CHECKSUM(DocumentId,DocumentSessionId)) + CHECKSUM(REVERSE(DocumentId),REVERSE(DocumentSessionId)) )
FROM DocumentOutPutItems

Ответ 3

В чем заключается ваш существующий запрос, который вам не нравится? Если вы обеспокоены тем, что DISTINCT через два столбца не возвращает только уникальные перестановки, почему бы не попробовать?

Это, безусловно, работает, как вы могли бы ожидать в Oracle.

SQL> select distinct deptno, job from emp
  2  order by deptno, job
  3  /

    DEPTNO JOB
---------- ---------
        10 CLERK
        10 MANAGER
        10 PRESIDENT
        20 ANALYST
        20 CLERK
        20 MANAGER
        30 CLERK
        30 MANAGER
        30 SALESMAN

9 rows selected.


SQL> select count(*) from (
  2  select distinct deptno, job from emp
  3  )
  4  /

  COUNT(*)
----------
         9

SQL>

изменить

Я спустился по тупику с аналитикой, но ответ был удручающе очевидным...

SQL> select count(distinct concat(deptno,job)) from emp
  2  /

COUNT(DISTINCTCONCAT(DEPTNO,JOB))
---------------------------------
                                9

SQL>

изменить 2

Учитывая следующие данные, приведенное выше конкатенирующее решение будет ошибочным:

col1  col2
----  ----
A     AA
AA    A

Итак, мы должны включить разделитель...

select col1 + '*' + col2 from t23
/

Очевидно, что выбранный разделитель должен быть символом или набором символов, которые никогда не могут отображаться ни в одном столбце.

Ответ 4

Как насчет чего-то типа:

select count(*)
from
  (select count(*) cnt
   from DocumentOutputItems
   group by DocumentId, DocumentSessionId) t1

Наверное, просто делает то же, что и вы, хотя это позволяет избежать DISTINCT.

Ответ 5

Чтобы выполнить один запрос, объедините столбцы, а затем получите различное количество экземпляров объединенной строки.

SELECT count(DISTINCT concat(DocumentId, DocumentSessionId)) FROM DocumentOutputItems;

В MySQL вы можете сделать то же самое без этапа объединения следующим образом:

SELECT count(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems;

Эта функция упоминается в документации MySQL:

http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count-distinct

Ответ 6

Здесь более короткая версия без подзапроса:

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems

Он отлично работает в MySQL, и я думаю, что оптимизатору легче понять это.

Изменить: По-видимому, я неправильно читаю MSSQL и MySQL - извините, но, возможно, это все равно помогает.

Ответ 7

Многие (большинство?) Базы данных SQL могут работать с кортежами, такими как значения, поэтому вы можете просто сделать: SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems; Если ваша база данных не поддерживает это, ее можно смоделировать в соответствии с предложением @Oncel-umut-Turer CHECKSUM или другой скалярной функции, обеспечивающей хорошую уникальность, например, COUNT(DISTINCT CONCAT(DocumentId, ':', DocumentSessionId)).

Связанное использование кортежей заключается в выполнении запросов IN таких как: SELECT * FROM DocumentOutputItems WHERE (DocumentId, DocumentSessionId) in (('a', '1'), ('b', '2'));

Ответ 8

В вашем запросе нет ничего плохого, но вы также можете сделать это следующим образом:

WITH internalQuery (Amount)
AS
(
    SELECT (0)
      FROM DocumentOutputItems
  GROUP BY DocumentId, DocumentSessionId
)
SELECT COUNT(*) AS NumberOfDistinctRows
  FROM internalQuery

Ответ 9

Надеюсь, что это работает, я пишу на prima vista​​p >

SELECT COUNT(*) 
FROM DocumentOutputItems 
GROUP BY DocumentId, DocumentSessionId

Ответ 10

Если у вас есть только одно поле для "DISTINCT", вы можете использовать:

SELECT COUNT(DISTINCT DocumentId) 
FROM DocumentOutputItems

и это возвращает тот же план запроса, что и оригинал, как было проверено с помощью SET SHOWPLAN_ALL ON. Однако вы используете два поля, чтобы вы могли попробовать что-то сумасшедшее, например:

    SELECT COUNT(DISTINCT convert(varchar(15),DocumentId)+'|~|'+convert(varchar(15), DocumentSessionId)) 
    FROM DocumentOutputItems

но у вас будут проблемы, если задействованы NULL. Я бы просто придерживался исходного запроса.

Ответ 11

Я нашел это, когда я Googled для своей собственной проблемы, обнаружил, что если вы подсчитаете объекты DISTINCT, вы получите правильный номер (я использую MySQL)

SELECT COUNT(DISTINCT DocumentID) AS Count1, 
  COUNT(DISTINCT DocumentSessionId) AS Count2
  FROM DocumentOutputItems

Ответ 12

Я использовал этот подход, и он сработал для меня.

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems

Для моего случая это дает правильный результат.

Ответ 13

Я хочу, чтобы MS SQL также могла делать что-то вроде COUNT (DISTINCT A, B). Но он не может.

Сначала ответ JayTee казался для меня решением после нескольких тестов. CHECKSUM() не смог создать уникальные значения. Быстрый пример: оба CHECKSUM (31,467,519) и CHECKSUM (69,1120,823) дают тот же ответ, который равен 55.

Затем я сделал некоторые исследования и обнаружил, что Microsoft НЕ рекомендует использовать CHECKSUM для целей обнаружения изменений. В некоторых форумах некоторые предложили использовать

SELECT COUNT(DISTINCT CHECKSUM(value1, value2, ..., valueN) + CHECKSUM(valueN, value(N-1), ..., value1))

но это также неудобно.

Вы можете использовать функцию HASHBYTES(), как предложено в головоломке TSQL CHECKSUM. Однако у этого также есть небольшой шанс не возвращать уникальные результаты.

Я бы предложил использовать

SELECT COUNT(DISTINCT CAST(DocumentId AS VARCHAR)+'-'+CAST(DocumentSessionId AS VARCHAR)) FROM DocumentOutputItems

Ответ 14

Меня устраивает. В оракуле

SELECT SUM(DECODE(COUNT(*),1,1,1))
FROM DocumentOutputItems GROUP BY DocumentId, DocumentSessionId;

В jpql:

SELECT SUM(CASE WHEN COUNT(i)=1 THEN 1 ELSE 1 END)
FROM DocumentOutputItems i GROUP BY i.DocumentId, i.DocumentSessionId;

Ответ 15

Как насчет этого,

Select DocumentId, DocumentSessionId, count(*) as c 
from DocumentOutputItems 
group by DocumentId, DocumentSessionId;

Это даст нам счет всех возможных комбинаций DocumentId и DocumentSessionId.

Ответ 16

У меня был похожий вопрос, но у меня был подзапрос с данными сравнения в основном запросе. что-то вроде:

Select code, id, title, name 
(select count(distinct col1) from mytable where code = a.code and length(title) >0)
from mytable a
group by code, id, title, name
--needs distinct over col2 as well as col1

игнорируя сложности этого, я понял, что не могу получить значение a.code в подзапрос с двойным подзапросом, описанным в исходном вопросе

Select count(1) from (select distinct col1, col2 from mytable where code = a.code...)
--this doesn't work because the sub-query doesn't know what "a" is

В итоге я понял, что могу обмануть и объединить столбцы:

Select count(distinct(col1 || col2)) from mytable where code = a.code...

Это то, что в конечном итоге работает

Ответ 17

Если вы работаете с типами данных фиксированной длины, вы можете привести к binary, чтобы сделать это очень легко и очень быстро. Предполагая, что DocumentId и DocumentSessionId оба являются int с, и, следовательно, имеют длину 4 байта...

SELECT COUNT(DISTINCT CAST(DocumentId as binary(4)) + CAST(DocumentSessionId as binary(4)))
FROM DocumentOutputItems

Моя конкретная проблема потребовала, чтобы я разделил SUM на COUNT отдельной комбинации различных внешних ключей и поля даты, сгруппировав по другому внешнему ключу и иногда фильтруя по определенным значениям или ключам. Таблица очень большая, и использование подзапроса значительно увеличило время запроса. И из-за сложности, статистика просто не была жизнеспособным вариантом. Решение CHECKSUM также было слишком медленным в своем преобразовании, особенно из-за различных типов данных, и я не мог рисковать его ненадежностью.

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

Ответ 18

Этот ниже запрос работал у меня в MySQL:

SELECT COUNT(DISTINCT col_1,col_2,..) from table_name;

Столбцы, приведенные в предыдущем запросе, а именно: col_1, col_2 имеет комбинированное ограничение UNIQUE. Это означает, что в моей таблице table_name я создал индекс UNIQUE на col_1 + col_2.

Ответ 19

Вы можете просто использовать функцию подсчета дважды.

В этом случае это будет:

SELECT COUNT (DISTINCT DocumentId), COUNT (DISTINCT DocumentSessionId) 
FROM DocumentOutputItems

Ответ 20

Этот код использует отличные по 2 параметрам и обеспечивает подсчет количества строк, специфичных для этих разных значений. Это работало для меня в MySQL как очарование.

select DISTINCT DocumentId as i,  DocumentSessionId as s , count(*) 
from DocumentOutputItems   
group by i ,s;