파티션을 구분하는 SQL 계산


10

두 개의 열이있는 테이블이 있으며 Col_A를 통해 Col_B의 고유 값을 계산하고 싶습니다.

MyTable

Col_A | Col_B 
A     | 1
A     | 1
A     | 2
A     | 2
A     | 2
A     | 3
b     | 4
b     | 4
b     | 5

예상 결과

Col_A   | Col_B | Result
A       | 1     | 3
A       | 1     | 3
A       | 2     | 3
A       | 2     | 3
A       | 2     | 3
A       | 3     | 3
b       | 4     | 2
b       | 4     | 2
b       | 5     | 2

나는 다음 코드를 시도했다

select *, 
count (distinct col_B) over (partition by col_A) as 'Result'
from MyTable

개수 (고유 한 col_B)가 작동하지 않습니다. 고유 한 값을 계산하기 위해 count 함수를 어떻게 다시 작성할 수 있습니까?

답변:


18

이것이 내가하는 방법입니다.

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY (   SELECT COUNT(DISTINCT mt2.Col_B) AS dc
                FROM   #MyTable AS mt2
                WHERE  mt2.Col_A = mt.Col_A
                -- GROUP BY mt2.Col_A 
            ) AS ca;

GROUP BY절은 질문에 제공되는 데이터를 중복 부여하지만, 당신에게 더 나은 실행 계획을 제공 할 수 있습니다. 후속 Q & A CROSS APPLY에서 외부 조인 생성을 참조하십시오 .

SQL Server에 해당 기능을 추가하려면 피드백 사이트의 집계 함수 에 대한 OVER 절 향상 요청-DISTINCT 절에 대한 투표를 고려하십시오 .


6

를 사용하여 에뮬레이션 dense_rank한 다음 각 파티션의 최대 순위를 선택할 수 있습니다 .

select col_a, col_b, max(rnk) over (partition by col_a)
from (
    select col_a, col_b
        , dense_rank() over (partition by col_A order by col_b) as rnk 
    from #mytable
) as t    

col_b와 동일한 결과를 얻으려면에서 null을 제외해야합니다 COUNT(DISTINCT).


6

이것은 어떤 방식으로 Lennart의 솔루션에 대한 확장 이지만, 편집으로 제안하지 않는 것이 너무 추합니다. 여기서 목표는 파생 테이블없이 결과를 얻는 것입니다. 그럴 필요가 없을 수도 있고, 질의의 추악함과 결합하여 모든 노력이 낭비되는 것처럼 보일 수 있습니다. 그래도 나는 이것을 운동으로하고 싶었고 이제 결과를 공유하고 싶습니다.

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - 1
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 0
                  ELSE 1
                  END
FROM
  dbo.MyTable
;

계산의 핵심 부분은 이것입니다 (그리고 우선 아이디어가 내 것이 아니라는 것을 알고 싶습니다. 다른 곳 에서이 트릭에 대해 배웠습니다).

  DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- 1

의 값에 Col_Bnull이 없는 경우이 표현식을 변경하지 않고 사용할 수 있습니다 . 그러나 열에 널이있을 수있는 경우이를 설명해야합니다. 이것이 바로 CASE표현식 의 표현입니다. 파티션 당 행 수와 파티션 당 Col_B 수를 비교합니다. 숫자가 다르면 일부 행에 널이 Col_B있으므로 초기 계산 ( DENSE_RANK() ... + DENSE_RANK() - 1)을 1 씩 줄여야 한다는 의미입니다 .

- 1는 핵심 수식의 일부 이기 때문에 나는 그것을 그대로두기로 선택했습니다. 그러나 실제로 CASE는 전체 솔루션을 덜보기 흉하게 보이도록 헛된 시도로 표현에 실제로 포함될 수 있습니다 .

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 1
                  ELSE 2
                  END
FROM
  dbo.MyTable
;

이 라이브 데모 에서 dbfiddle 로고용액의 양 변화를 테스트하는 데 사용할 수있는 DB <> fiddle.uk.


2
create table #MyTable (
Col_A varchar(5),
Col_B int
)

insert into #MyTable values ('A',1)
insert into #MyTable values ('A',1)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',3)

insert into #MyTable values ('B',4)
insert into #MyTable values ('B',4)
insert into #MyTable values ('B',5)


;with t1 as (

select t.Col_A,
       count(*) cnt
 from (
    select Col_A,
           Col_B,
           count(*) as ct
      from #MyTable
     group by Col_A,
              Col_B
  ) t
  group by t.Col_A
 )

select a.*,
       t1.cnt
  from #myTable a
  join t1
    on a.Col_A = t1.Col_a

1

상관 관계가있는 하위 쿼리 (Erik Darling의 답변)와 CTE (kevinnwhat의 답변)에 약간 알레르기가있는 경우 대안.

널 (null)이 믹스에 던져지면 이들 중 어느 것도 원하는 방식으로 작동하지 않을 수 있습니다. (그러나 맛을 내기 위해 수정하는 것은 매우 간단합니다)

간단한 경우 :

--ignore the existence of nulls
SELECT [mt].*, [Distinct_B].[Distinct_B]
FROM #MyTable AS [mt]

INNER JOIN(
    SELECT [Col_A], COUNT(DISTINCT [Col_B]) AS [Distinct_B]
    FROM #MyTable
    GROUP BY [Col_A]
) AS [Distinct_B] ON
    [mt].[Col_A] = [Distinct_B].[Col_A]
;

위와 동일하지만 null 처리를 위해 무엇을 변경해야하는지에 대한 의견이 있습니다.

--customizable null handling
SELECT [mt].*, [Distinct_B].[Distinct_B]
FROM #MyTable AS [mt]

INNER JOIN(
    SELECT 

    [Col_A],

    (
        COUNT(DISTINCT [Col_B])
        /*
        --uncomment if you also want to count Col_B NULL
        --as a distinct value
        +
        MAX(
            CASE
                WHEN [Col_B] IS NULL
                THEN 1
                ELSE 0
            END
        )
        */
    )
    AS [Distinct_B]

    FROM #MyTable
    GROUP BY [Col_A]
) AS [Distinct_B] ON
    [mt].[Col_A] = [Distinct_B].[Col_A]
/*
--uncomment if you also want to include Col_A when it's NULL
OR
([mt].[Col_A] IS NULL AND [Distinct_B].[Col_A] IS NULL)
*/
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.