SQL에서 중앙값(MEDIAN) 계산

소개


SQL에서 중앙값을 산출하는 방법은, 사용하기 때문에 성능이 매우 나쁜 것 같습니다.

거기서, 퍼포먼스를 개선하는 방법으로서, 건수의 집계를 실시하고 나서 자기 결합하는 방법을 생각해 왔으므로 소개합니다.

해설


간단한 샘플 데이터로 짝수 짝수 건수와 홀수 홀수 건 데이터를 준비합니다.

짝수 데이터






1

1

2

3

홀수건 데이터






1

1

2

3

3

(1) 우선 값별로 데이터 건수를 집계한다

SELECT
  
, COUNT(*) AS 件数
FROM 偶数件データ
GROUP BY 
전술 한 테이블은 뷰, 임시 테이블 또는 서브 쿼리로서 후속 SQL에서 집계 데이터로 표현된다.

짝수 개의 데이터 실행 결과




건수


1
2

2
1

3
1

홀수건 데이터 실행 결과




건수


1
2

2
1

3
2

(2) 건수의 누적 계산

SELECT
  Z.
, Z.件数
, SUM(A.件数) AS 累積件数
FROM 集計データ AS Z,
     集計データ AS A
WHERE Z. >= A.
GROUP BY
  Z.
, Z.件数

짝수 개의 데이터 실행 결과




건수
누적 건수


1
2
2

2
1
3

3
1
4

홀수건 데이터 실행 결과




건수
누적 건수


1
2
2

2
1
3

3
2
5

(3) 누적 전 건수(해당 레코드의 건수를 가산하기 전의 건수)와 중간 건수(전체의 절반에 해당하는 건수)를 합쳐서 표시한다

SELECT
  Z.
, Z.件数
, SUM(A.件数) - Z.件数 AS 累積前件数
, SUM(A.件数) AS 累積件数
, (SELECT SUM(件数)/2 FROM 集計データ) AS 中間件数
FROM 集計データ AS Z,
     集計データ AS A
WHERE Z. >= A.
GROUP BY
  Z.
, Z.件数

짝수 개의 데이터 실행 결과




건수
누적 전 건수
누적 건수
중간 건수


1
2
0
2
2

2
1
2
3
2

3
1
3
4
2

홀수건 데이터 실행 결과




건수
누적 전 건수
누적 건수
중간 건수


1
2
0
2
2.5

2
1
2
3
2.5

3
2
3
5
2.5

(4) 누적 전 건수 ≤ 중간 건수 ≤ 누적 건수를 만족하는 레코드를 추출한다

SELECT *
FROM 
	(SELECT
	  Z.
	, Z.件数
	, SUM(A.件数) - Z.件数 AS 累積前件数
	, SUM(A.件数) AS 累積件数
	, (SELECT SUM(件数)/2 FROM 集計データ) AS 中間件数
	FROM 集計データ AS Z,
	     集計データ AS A
	WHERE Z. >= A.
	GROUP BY
	  Z.
	, Z.件数) AS SUB
WHERE 中間件数 BETWEEN 累積前件数 AND 累積件数

짝수 개의 데이터 실행 결과




건수
누적 전 건수
누적 건수
중간 건수


1
2
0
2
2

2
1
2
3
2

※1레코드 또는 2레코드가 대상이 됩니다.

홀수건 데이터 실행 결과




건수
누적 전 건수
누적 건수
중간 건수


2
1
2
3
2.5

※반드시 1레코드만이 대상이 됩니다.

(5) (4)에서 추출한 레코드의 값의 평균을 산출한다.

SELECT AVG() AS 中央値
FROM 
    (SELECT
      Z.
    , Z.件数
    , SUM(A.件数) AS 累積件数
    , (SELECT SUM(件数) FROM 集計データ) AS 総件数
    FROM 集計データ AS Z,
         集計データ AS A
    WHERE Z. >= A.
    GROUP BY
      Z.
    , Z.件数) AS SUB
WHERE 総件数/2 BETWEEN 累積件数 - 件数  AND 累積件数
※일부 표현을 깨끗이 하기 위해서, 계산 순서를 바꾸고 있습니다.

짝수 개의 데이터 실행 결과



중앙값


1.5

홀수건 데이터 실행 결과



중앙값


2

여기 기사의 질문 4의 실행 결과


(3)의 실행 결과가 이하와 같고, 노란색의 2 레코드가 (4)의 추출 대상이 되고, 결과는 220000과 235000의 평균으로 227500이 됩니다
여기

참고로 분석 함수를 사용한 SQL의 예는 다음과 같습니다.

WITH SUB AS (
  SELECT
    
  , 件数
  , SUM(件数) OVER( ORDER BY ) AS 累積件数
  , SUM(件数) OVER() AS 総件数
  FROM 集計データ
)

SELECT AVG() AS 中央値
FROM SUB
WHERE 総件数/2 BETWEEN 累積件数 - 件数 AND 累積件数

잘 생각하면, 분석 함수를 사용한 경우는 자기 결합이 불필요하게 되므로, 집계 데이터를 일부러 사용할 필요도 없기 때문에 이하의 내용으로 가능합니다

WITH SUB AS (
  SELECT
    
  , ROW_NUMBER() OVER (ORDER BY ) AS 連番
  , COUNT(*) OVER () AS 総件数
  FROM 元テーブル
)

SELECT AVG() AS 中央値
FROM SUB
WHERE 総件数/2 BETWEEN 連番 - 1 AND 連番

좋은 웹페이지 즐겨찾기