여러 열에 대한 DISTINCT 계산


213

다음과 같은 쿼리를 수행하는 더 좋은 방법이 있습니까?

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

이 테이블에서 고유 항목 수를 계산해야하지만 고유 항목이 두 열 이상입니다.

내 쿼리는 정상적으로 작동하지만 하나의 쿼리 (하위 쿼리를 사용하지 않고)를 사용하여 최종 결과를 얻을 수 있는지 궁금합니다.


IordanTanev, Mark Brackett, RC-답장을 보내 주셔서 감사합니다. 좋은 시도 였지만 SO에 게시하기 전에 수행중인 작업을 확인해야합니다. 제공 한 검색어가 내 검색어와 다릅니다. 항상 스칼라 결과가 있지만 쿼리가 여러 행을 반환한다는 것을 쉽게 알 수 있습니다.
Novitzky

답변 중 하나의 명확한 설명을 포함하도록 질문을 업데이트했습니다.
Jeff


좋은 질문입니다. 이 작업을 수행하는 더 간단한 방법이 있는지 궁금합니다.
Anupam

답변:


73

성능을 향상시키려는 경우 두 열의 해시 또는 연결된 값에서 지속 계산 열을 만들 수 있습니다.

열이 결정적이며 "정확한"데이터베이스 설정을 사용하는 경우 지속되면,이를 색인화하거나 통계를 작성할 수 있습니다.

계산 된 열의 고유 한 수가 쿼리와 동일하다고 생각합니다.


4
훌륭한 제안! 더 많이 읽을수록 SQL이 구문과 함수를 알지 못하고 순수한 논리를 적용하는 것에 대해 더 많이 알지 못한다는 것을 깨달았습니다.
tumchaaditya

너무 좋은 제안입니다. 불필요한 코드를 작성하지 않았습니다.
Avrajit Roy

1
이것이 의미하는 것과 그 방법에 대해 더 많은 것을 보여주기 위해 예제 또는 코드 샘플을 추가 하시겠습니까?
jayqui

52

편집 : 신뢰할 수없는 체크섬 전용 쿼리 에서 변경되었습니다 .SQL Server 2005 에서이 작업을 수행하는 방법을 찾았습니다. (SQL Server 2005에서) CHECKSUM () 함수). REVERSE () 함수는 int를 varchar로 변환하여 구별을보다 안정적으로 만듭니다.

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

1
+1 멋진 하나는 완벽하게 작동합니다 (당신은 ...에 체크섬을 수행 할 수있는 권리 열 유형이있는 경우)
베르누이 IT

8
Checksum ()과 같은 해시를 사용하면 다른 입력에 대해 동일한 해시가 반환 될 가능성이 적으므로 카운트가 약간 떨어질 수 있습니다. HashBytes ()는 훨씬 적은 기회이지만 여전히 0은 아닙니다. 이 두 ID가 int (32b) 인 경우 "lossless hash"는 Id1 << 32 + Id2와 같은 bigint (64b)로 결합 할 수 있습니다.
crokusek

1
특히 열을 결합하기 시작할 때 (그것이 의도했던 것임) 가능성은 그리 작지 않습니다. 이 접근 방식에 대해 궁금했고 특정 경우 체크섬의 수가 10 % 줄었습니다. 조금 더 오래 생각하면 Checksum은 int를 반환하므로 전체 bigint 범위를 체크섬하면 실제보다 약 20 억 배 작은 고유 카운트로 끝납니다. -1
pvolders

중복 가능성을 제거하기 위해 "REVERSE"를 사용하도록 쿼리를 업데이트했습니다.
JayTee

4
CHECKSUM을 피할 수 있습니까? 두 값을 함께 연결할 수 있습니까? 나는 위험을 같은 것으로 간주한다고 가정합니다 : ( 'he', 'art') == 'hear', 't'). 그러나 @APC가 제안하는 것처럼 구분 기호로 해결할 수 있다고 생각합니다 (두 열에 표시되지 않는 일부 값). 접근하다?
The Red Pea

31

기존 검색어에 대해 마음에 들지 않는 것은 무엇입니까? DISTINCT두 열에 걸쳐 고유 순열 만 반환하지 않는 것이 염려되는 경우 시도해보십시오.

오라클에서 예상 한대로 작동합니다.

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
/

분명히 선택한 구분 기호는 문자 또는 문자 집합이어야하며 어느 열에도 표시되지 않아야합니다.


나에게서 +1 답변 주셔서 감사합니다. 내 쿼리는 제대로 작동하지만 하위 쿼리를 사용하지 않고 하나의 쿼리 만 사용하여 최종 결과를 얻을 수 있는지 궁금합니다.
Novitzky

20

단일 쿼리로 실행하려면 열을 연결 한 다음 연결된 문자열의 고유 한 인스턴스 수를 가져옵니다.

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


이것은 SQL Server 질문이며 게시 한 두 옵션 모두이 질문에 대한 답변에서 이미 언급되었습니다 : stackoverflow.com/a/1471444/4955425stackoverflow.com/a/1471713/4955425 .
sstan

1
FWIW는 PostgreSQL에서 거의 작동합니다. 추가 괄호가 필요합니다.SELECT COUNT(DISTINCT (DocumentId, DocumentSessionId)) FROM DocumentOutputItems;
ijoseph

14

어때요?

카운트 선택 (*)
...에서
  (선택 카운트 (*) cnt
   DocumentOutputItems에서
   DocumentId, DocumentSessionId별로 그룹화) t1

아마도 당신이 이미했던 것과 똑같은 일을하지만 DISTINCT를 피합니다.


내 테스트에서 (SET SHOWPLAN_ALL ON 사용) 동일한 실행 계획과 정확히 동일한 TotalSubtreeCost
KM을

1
원래 쿼리의 복잡성에 따라이 문제를 해결 GROUP BY하면 원하는 결과를 얻기 위해 쿼리 변환에 몇 가지 추가 문제 가 발생할 수 있습니다 (예 : 원래 쿼리에 이미 GROUP BY또는 HAVING절이있는 경우 ...)
Lukas Eder

8

subselect가없는 짧은 버전은 다음과 같습니다.

SELECT COUNT(DISTINCT DocumentId, DocumentSessionId) FROM DocumentOutputItems

MySQL에서 잘 작동하며 최적화 프로그램이이를 이해하는 데 더 쉬운 시간이 있다고 생각합니다.

편집 : 분명히 MSSQL과 MySQL을 잘못 읽었습니다. 죄송하지만 어쨌든 도움이 될 수 있습니다.


6
SQL Server에서는 다음과 같은 메시지가 나타납니다
KM.

이것이 내가 생각한 것입니다. 가능하다면 MSSQL에서도 비슷한 일을하고 싶습니다.
Novitzky

@ Kamil Nowicki의 SQL Server에서는 COUNT ()에 하나의 필드 만 가질 수 있습니다. 제 대답에 두 필드를 하나로 연결 하고이 접근법을 시도 할 수 있음을 보여줍니다. 그러나 쿼리 계획이 동일하기 때문에 원본을 고수했습니다.
KM.

1
@JayTee 답변을 살펴보십시오. 그것은 매력처럼 작동합니다. count ( distinct CHECKSUM ([Field1], [Field2])
Custodio

5

많은 (대부분?) 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'));


어떤 데이터베이스를 지원 select count(distinct(a, b))합니까? : D
Vytenis Bivainis

@VytenisBivainis PostgreSQL이 알고있는 버전을 알고 있습니다.
karmakaze

3

쿼리에는 아무런 문제가 없지만 다음과 같이 할 수도 있습니다.

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

3

희망이 프리마 비스타에 쓰고 있어요

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

7
이것이 최종 답변을 제공하려면 다른 SELECT COUNT (*) FROM (...)으로 감싸 야합니다. 본질적 으로이 답변은 계산하려는 고유 값을 나열하는 다른 방법을 제공합니다. 원래 솔루션보다 낫지 않습니다.
Dave Costa

고마워 데이브. 내 경우에는 별개 대신 그룹을 사용할 수 있다는 것을 알고 있습니다. 하나의 쿼리를 사용하여 최종 결과를 얻는 지 궁금합니다. 불가능하다고 생각하지만 틀렸을 수도 있습니다.
Novitzky

3

나는이 접근법을 사용했고 그것은 나를 위해 일했다.

SELECT COUNT(DISTINCT DocumentID || DocumentSessionId) 
FROM  DocumentOutputItems

내 경우에는 올바른 결과를 제공합니다.


두 열과 함께 고유 한 값의 개수를 제공하지 않습니다. 적어도 MySQL 5.8에서는 그렇지 않습니다.
Anwar Shaikh

이 질문은 SQL Server로 태그되어 있으며 SQL Server 구문이 아닙니다
Tab Alleman

2

"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이 관련되면 문제가 있습니다. 원래 쿼리를 그대로 사용합니다.


나에게서 +1 고맙지 만 제안한대로 내 질문을 고수 할 것입니다. "변환"을 사용하면 성능이 훨씬 저하 될 수 있습니다.
Novitzky

2

나는 내 자신의 문제에 대해 Google 검색 할 때 이것을 발견했으며 DISTINCT 객체를 계산하면 올바른 숫자가 반환된다는 것을 알았습니다 (MySQL을 사용하고 있습니다)

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

5
위의 쿼리는 영업 이익 (뚜렷한 찾고 있던 것보다 결과의 다른 세트를 반환 조합DocumentIdDocumentSessionId). OP가 MS SQL Server가 아닌 MySQL을 사용하는 경우 Alexander Kjäll은 이미 정답을 게시했습니다.
Anthony Geoghegan

1

MS SQL이 COUNT (DISTINCT A, B)와 같은 것을 할 수 있기를 바랍니다. 그러나 그것은 할 수 없습니다.

처음에 JayTee의 답변은 일부 테스트 CHECKSUM ()이 고유 값을 생성하지 못한 후 나에게 해결책처럼 보였다. CHECKSUM (31,467,519)과 CHECKSUM (69,1120,823) 모두 55라는 동일한 대답을 제공합니다.

그런 다음 약간의 연구를 통해 변경 감지 목적으로 CHECKSUM을 사용하지 않는 것이 좋습니다. 일부 포럼에서 일부는

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

그러나 이것은 또한 편안하지 않습니다.

TSQL CHECKSUM conundrum 에서 제안한대로 HASHBYTES () 함수를 사용할 수 있습니다. . 그러나 이것은 또한 고유 한 결과를 반환하지 않을 가능성이 적습니다.

나는 사용하는 것이 좋습니다

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

1

이건 어때요,

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

그러면 DocumentId 및 DocumentSessionId의 가능한 모든 조합 수를 얻을 수 있습니다.


0

그것은 나를 위해 작동합니다. 오라클에서 :

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;

0

비슷한 질문이 있었지만 내가 가진 쿼리는 주 쿼리의 비교 데이터가 포함 된 하위 쿼리였습니다. 같은 :

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...

이것은 결국 작동하는 것입니다


0

고정 길이의 데이터 유형으로 작업하는 binary경우이 작업을 매우 쉽고 빠르게 수행 할 수 있습니다 . 가정 DocumentId하고 DocumentSessionId모두 ints이므로 4 바이트 길이입니다 ...

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

내 특정 문제는 분할 나를 필요 SUM에 의해 COUNT다른 외래 키에 의해 그룹화 때때로 특정 값 또는 키에 의해 필터링, 다양한 외부 키의 독특한 조합 및 날짜 필드의. 테이블이 매우 커서 하위 쿼리를 사용하면 쿼리 시간이 크게 늘어납니다. 그리고 복잡성 때문에 통계는 단순히 실행 가능한 옵션이 아니 었습니다. 그만큼CHECKSUM솔루션은 특히 다양한 데이터 유형의 결과로, 그 변환에 너무 느린도 있었고, 나는 그것의 신뢰성 위험을 감수 할 수 없었다.

그러나 위의 솔루션을 사용하면 쿼리 시간이 거의 증가하지 않았으며 (단순히를 사용하는 것과 비교하여 SUM) 완전히 신뢰할 수 있어야합니다! 비슷한 상황에서 다른 사람들을 도울 수 있어야하므로 여기에 게시하고 있습니다.


-1

Count Function Twice를 사용할 수 있습니다.

이 경우 다음과 같습니다.

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

이것은 질문에서 필요로하는 것이 아니라, 각 열에 대해 별개로 계산합니다
naviram

-1

이 코드는 2 개의 매개 변수에서 distinct를 사용하고 해당 고유 값 행 수에 특정한 행 수를 제공합니다. 그것은 MySQL처럼 매력처럼 작동했습니다.

select DISTINCT DocumentId as i,  DocumentSessionId as s , count(*) 
from DocumentOutputItems   
group by i ,s;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.