024. 레코드에 순번 붙이기 응용 - llighter/database GitHub Wiki
테이블의 레코드에 순번을 붙일 수 있다면,SQL에서 자연 수열(순번)의 성질을 활용한 다양한 테크닉을 사용할 수 있다.
1. 중앙값 구하기
통계의 지표로 사용되는 중앙값(median, 중간값)을 구하는 방법을 생각해보자.
중앙값은 평균과 단순 평균(mean)과 다르게 아웃라이어에 영향을 받지 않는다는 장점이 있다.
- 집합 지향적 방법
-- 중앙값 구하기(집합 지향적 방법): 모집합을 상위와 하위로 분할
SELECT AVG(weight)
FROM (SELECT W1.weight
FROM Weights W1, Weights W2
GROUP BY W1.weight
-- S1(하위 집합)의 조건
HAVING SUM(CASSE WHEN W2.weight >= W1.weight THEN 1 ELSE 0 END)
>= COUNT(*) / 2
-- S2(상위 집합)의 조건
AND SUM(CASE WHEN W2.weight <= W1.weight THEN 1 ELSE 0 END)
>= COUNT(*) / 2) TMP;
위 방법의 두 가지 단점
- 코드가 복잡해서 무엇을 하고 있는 것인지 이해하기 힘들다.
- 성능이 나쁘다.
- 절차 지향적 방법 1 - 세계의 중심을 향해
-- 중앙값 구하기(절차 지향형 1): 양쪽 끝에서 레코드 하나씩 세어 중간을 찾음
SELECT AVG(weight) AS median
FROM (SELECT weight,
ROW_NUMBER() OVER(ORDER BY weight ASC, student_id ASC) AS hi,
ROW_NUMBER() OVER(ORDER BY weight DESC, student_id DESC) AS lo
FROM Weights) TMP
WHERE hi In(lo, lo+1, lo-1);
- 절차 지향적 방법 2 - 2 빼기 1은 1
ROW_NUMBER
를 사용한 것 보다 성능을 개선하기
-- 중앙값 구하기(절차 지향적 방법) 2 : 반환점 발견
SELECT AVG(weight)
FROM (SELECT weight,
2 * ROW_NUMBER() OVER(ORDER BY weight)
- COUNT(*) OVER() AS diff
FROM Weights) TMP
WHERE diff BETWEEN 0 AND 2;
2. 순번을 사용한 테이블 분할
- 단절 구간 찾기
레스토랑에서 테이블 번호가 빠져 있는 것은 예약된 테이블이라는 것이다.
빠져 있는 테이블을 gap_start
~ gap_end
와 같은 테이블 형식으로 구하라.
- 집합 지향적 방법 - 집합의 경계선
-- 비어있는 숫자 모음을 표시
SELECT (N1.num + 1) AS gap_start,
'~',
(MIN(N2.num) - 1) AS gap_end
FROM Numbers N1 INNER JOIN Numbers N2
ON N2.num > N2.num
GROUP BY N1.num
HAVING (N1.num + 1) < MIN(M2.num);
- 절차 지향적 방법 - '다음 레코드'와 비교
-- 다음 레코드와 비교
SELECT num + 1 AS gap_start
'-',
(num + diff -1) AS gap end
FROM (SELECT num,
MAX(num)
OVER(ORDER BY num
ROWS BETWEEN 1 FOLLOWING
AND 1 FOLLOWING - num
FROM Numbers) TMP(num, diff)
WHERE diff <> 1;