DISTINCT를 사용하여 파티션 함수 COUNT () OVER 가능


88

다음과 같이 고유 한 NumUsers의 누적 합계를 얻기 위해 다음을 작성하려고합니다.

NumUsers = COUNT(DISTINCT [UserAccountKey]) OVER (PARTITION BY [Mth])

관리 스튜디오는 이것에 대해 너무 행복해 보이지 않습니다. DISTINCT키워드를 제거하면 오류가 사라지 지만 고유 카운트는 아닙니다.

DISTINCT파티션 기능 내에서 가능하지 않은 것 같습니다. 고유 개수를 찾는 방법은 무엇입니까? 상관 하위 쿼리와 같은 보다 전통적인 방법을 사용 합니까?

좀 더 자세히 살펴보면 이러한 OVER함수는 SQL-Server누계 계산에 사용할 수 없다는 점에서 Oracle과 다르게 작동 할 수 있습니다 .

여기 SQLfiddle 에 실제 예제를 추가했습니다. 여기서 분할 함수를 사용하여 누적 합계를 계산하려고합니다.


2
COUNTORDER BY대신 PARTITION BY2008 년에 잘못 정의되어 나는 당신이 전혀 그것을 가지고시키는 것 놀라게하고있다. 당 문서 , 당신은 허용하지 않을 ORDER BY집계 함수에 대한.
Damien_The_Unbeliever

네-일부 오라클 기능과 혼동되고 있다고 생각합니다. 이러한 누계와 실행 횟수는 조금 더 관련 될 것입니다.
whytheq

답변:


177

사용하는 매우 간단한 솔루션이 있습니다. dense_rank()

dense_rank() over (partition by [Mth] order by [UserAccountKey]) 
+ dense_rank() over (partition by [Mth] order by [UserAccountKey] desc) 
- 1

이것은 당신이 요구했던 것을 정확하게 제공 할 것입니다 : 매월 고유 한 UserAccountKey의 수.


23
주의해야 할 한 가지는 dense_rank()NULL을 계산하지만 COUNT(field) OVER그렇지 않다는 것입니다. 이 때문에 내 솔루션에 사용할 수 없지만 여전히 영리하다고 생각합니다.
bf2020

1
하지만 매년 몇 달 동안 고유 한 useraccountkeys의 누적 합계를 찾고 있습니다. 이것이 어떻게 대답하는지 잘 모르겠습니까?
whytheq 2016-08-04

4
@ bf2020,에 NULL값 이있을 수있는 경우 UserAccountKey다음 용어를 추가해야합니다 -MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth).. 아이디어는 아래 LarsRönnbäck의 답변에서 가져옵니다. 경우 기본적으로, UserAccountKeyNULL값을, 당신은 추가 뺄 필요가 1있기 때문에, 결과에서 DENSE_RANK카운트 NULL을.
Vladimir Baranov

1
@ahsteele 당신에게 감사합니다 사람, 당신은 내 마음을 날려 내 문제 해결
엔리케 도나티

여기에서는 dense_rank창 함수에 프레임 이 있을 때이 솔루션 을 사용하는 방법에 대해 설명 합니다. SQL Server는 dense_rank창 프레임과 함께 사용하는 것을 허용하지 않습니다 . stackoverflow.com/questions/63527035/…
K4M

6

네크 로맨싱 :

DENSE_RANK를 통해 MAX로 PARTITION BY보다 COUNT DISTINCT를 에뮬레이션하는 것은 비교적 간단합니다.

;WITH baseTable AS
(
    SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR3' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR2' AS ADR
)
,CTE AS
(
    SELECT RM, ADR, DENSE_RANK() OVER(PARTITION BY RM ORDER BY ADR) AS dr 
    FROM baseTable
)
SELECT
     RM
    ,ADR

    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY ADR) AS cnt1 
    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM) AS cnt2 
    -- Not supported
    --,COUNT(DISTINCT CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY CTE.ADR) AS cntDist
    ,MAX(CTE.dr) OVER (PARTITION BY CTE.RM ORDER BY CTE.RM) AS cntDistEmu 
FROM CTE

참고 :
이는 해당 필드가 NON 허용 필드가 아니라고 가정합니다.
필드에 하나 이상의 NULL 항목이있는 경우 1을 빼야합니다.


5

위 의 David 와 유사한 솔루션을 사용 하지만 일부 행을 개수에서 제외해야하는 경우 추가 비틀기가 필요합니다. 이것은 [UserAccountKey]가 null이 아니라고 가정합니다.

-- subtract an extra 1 if null was ranked within the partition,
-- which only happens if there were rows where [Include] <> 'Y'
dense_rank() over (
  partition by [Mth] 
  order by case when [Include] = 'Y' then [UserAccountKey] else null end asc
) 
+ dense_rank() over (
  partition by [Mth] 
  order by case when [Include] = 'Y' then [UserAccountKey] else null end desc
)
- max(case when [Include] = 'Y' then 0 else 1 end) over (partition by [Mth])
- 1

확장 된 예제가있는 SQL Fiddle은 여기에서 찾을 수 있습니다.


1
여러분의 생각은 (복잡성없이 원래 공식 확인 할 수 있습니다 [Include]당신은 당신의 대답에 대해 이야기하는 것을) dense_rank()일 때 UserAccountKey이 될 수 있습니다 NULL. 다음 용어를 공식에 추가하십시오 -MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)..
Vladimir Baranov

5

SQL-Server 2008R2에서이 작업을 수행하는 유일한 방법은 상관 하위 쿼리 또는 외부 적용을 사용하는 것입니다.

SELECT  datekey,
        COALESCE(RunningTotal, 0) AS RunningTotal,
        COALESCE(RunningCount, 0) AS RunningCount,
        COALESCE(RunningDistinctCount, 0) AS RunningDistinctCount
FROM    document
        OUTER APPLY
        (   SELECT  SUM(Amount) AS RunningTotal,
                    COUNT(1) AS RunningCount,
                    COUNT(DISTINCT d2.dateKey) AS RunningDistinctCount
            FROM    Document d2
            WHERE   d2.DateKey <= document.DateKey
        ) rt;

이는 제안한 구문을 사용하여 SQL-Server 2012 에서 수행 할 수 있습니다 .

SELECT  datekey,
        SUM(Amount) OVER(ORDER BY DateKey) AS RunningTotal
FROM    document

그러나 사용 DISTINCT은 여전히 ​​허용되지 않으므로 DISTINCT가 필요하거나 업그레이드가 옵션이 아닌 경우 OUTER APPLY최선의 선택 이라고 생각 합니다.


감사합니다. 나는 내가 시도 할 OUTER APPLY 옵션을 특징으로하는 이 답변 을 찾았습니다 . 그 대답에서 반복되는 UPDATE 접근 방식을 보았습니까 ... 그것은 꽤 멀고 분명히 빠릅니다. 2012 년에는 삶이 더 쉬워 질 것입니다. 이것이 바로 Oracle 사본입니까?
whytheq
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.