전문가칼럼

DBMS, DB 구축 절차, 빅데이터 기술 칼럼, 사례연구 및 세미나 자료를 소개합니다.

마농의 SQL 백문백답 : 분석함수(Analytic Function)(3)

전문가칼럼
DBMS별 분류
Oracle
작성자
dataonair
작성일
2015-11-27 00:00
조회
10922




◎ 연재기사 ◎


마농의 SQL 백문백답: 단순하고 쉽게 작성하는 SQL 노하우 [1회]


마농의 SQL 백문백답: 단순하고 쉽게 작성하는 SQL 노하우 [2회]


마농의 SQL 백문백답 : 계층 구조 쿼리의 이해(3)


마농의 SQL 백문백답: 계층 구조 쿼리의 이해(4)


마농의 SQL 백문백답: 분석함수(Analytic Function)


마농의 SQL 백문백답: 분석함수(Analytic Function)(2)


마농의 SQL 백문백답 : 분석함수(Analytic Function)(3)


마농의 SQL 백문백답 : 분석함수(Analytic Function)(4)


마농의 SQL 백문백답: 분석함수(Analytic Function)(5)


마농의 SQL 백문백답: 분석함수(Analytic Function)(6)



마농의 SQL 백문백답: 단순하고 쉽게 작성하는 SQL 노하우 [7회]

분석함수(Analytic Function)(3)



단순하고 쉽게 작성하는 SQL 노하우를 공유하자!
SQL은 필자에게 있어서 참으로 이해하기 쉬운 프로그램 언어였다. 간단한 기본 구문(SELECT, FROM, WHERE, GROUP BY, HAVING)과 조인을 통해 안 되는 게 없을 정도였기 때문이다. 물론 기본 구문 외에 다양한 함수들은 따로 배워야 하겠지만 말이다. 필자가 Q&A 게시판에 올라오는 수많은 질문에 답하면서 느낀 점은 ‘질문을 올린 사람은 SQL을 어렵게 생각하고 있었다’는 것이었다.

앞서 언급한 기본 구문들은 정말 너무나도 간단한데, 이 간단한 구문을 복잡하게 사용하고 있었던 것이다. 필자가 다루는 SQL은 전혀 복잡하지 않다. 필자는 SQL을 단순하고 쉽게 작성하는 노하우를 이 페이지를 통해 독자들과 공유하고자 한다. 자주 올라오는 SQL 질문들, 독자들이 어려워했던 질문들의 해법을 제시하고자 한다.



지난 시간에는 분석함수 중 이전/이후 행을 조회할 수 있는 LAG/LEAD 함수를 살펴보았다. 또한 분석함수의 계산 범위를 한정짓는 Window 절 사용법도 함께 배웠다. 이번 시간에도 분석함수의 다른 기능들을 살펴볼 생각이다. 분석함수로 풀어낼 수 있는 다양한 문제의 해법을 제시하고, 이를 통해 분석함수에 대해 배워보는 시간을 갖도록 하겠다.



[질문 29] 사원의 연봉 순위를 구하고 싶어요.

이번 문제는 순위를 구하는 문제다. 분석함수 중 RANK 함수를 알고 있다면 간단히 해결되는 문제다.



SELECT empno, sal
, RANK() OVER(ORDER BY sal DESC) rk
FROM emp
;



column_img_2228.jpg

구문은 앞서 배운 분석함수 구문과 동일하다. 순위를 구하는 RANK 와 분석함수임을 나타내는 OVER 구문을 사용했다.

우리는 구문 이외에 RANK 함수의 결과를 눈여겨 볼 필요가 있다. 결과를 보면 연봉이 같은 사원은 동순위로 표시가 된다. 그리고 동순위의 다음 순위는 중간 순위를 건너뛰게 된다. 2등이 2명이면 다음 순위는 3등이 아닌 4등이 된다.

순위 함수에서 정렬항목은 순위를 구하는 기준이 된다. 따라서 ORDER BY 구문은 생략할 수 없다. 또 한 가지 특징은 계산 범위를 한정하는 WINDOW 절을 사용하지 못한다는 것이다. PARTITION BY 구문은 사용할 수 있다. 그룹별 순위를 구할 때 PARTITION BY 구문이 사용된다.



-- 1. Order By 절 필수 입력(생략 불가)
SELECT empno, sal
, RANK() OVER( ) rk
FROM emp
;
ORA-30485: 윈도우 지정에 ORDER BY 표현식이 없습니다



-- 2. Window 절 사용 불가
SELECT empno, sal
, RANK() OVER(ORDER BY sal DESC
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) rk
FROM emp
;
ORA-00907: 누락된 우괄호



-- 3. PARTITION BY 절 사용 가능
SELECT deptno, empno, sal
, RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) rk
FROM emp
;



column_img_2229.jpg

[질문 30] 동순위와 관계없이 순위가 이어져서 나오게 하고 싶어요.

29번의 문제와 달리 2등이 동순위가 있더라도 3등을 건너뛰지 말아야 한다. 이번 문제는 DENSE_RANK 사용에 관한 것이다.



SELECT empno, sal
, DENSE_RANK() OVER(ORDER BY sal DESC) dr
FROM emp
;



column_img_2230.jpg

RANK 와는 다른 형태로 순위가 출력됐다. 2등이 동순위 2명이고, 바로 다음 순위는 4등이 아닌 3등이 된다.



[질문 31] 동순위를 무시하고 모두 다른 순번을 부여하고 싶어요.

이번에는 RANK, DENSE_RANK 가 아닌 또 다른 순위 함수가 필요하다. 이번 문제는 ROW_NUMBER 사용에 관한 것이다.



SELECT empno, sal
, ROW_NUMBER() OVER(ORDER BY sal DESC) rn
FROM emp
;



column_img_2231.jpg

순위를 구하는 분석함수 세 가지를 배웠다.



[질문 32] 분석함수 없이 순위를 구할 수 있을까

RANK 함수를 이용해 손쉽게 순위를 구할 수 있다. 그런데 이 분석함수를 사용할 수 없다면, 어떻게 순위를 구할 수 있을까 이런 상황은 의외로 자주 접하게 된다. 분석함수를 지원하지 않는 DBMS를 사용하는 경우다.

이 문제를 풀기 위해서는 ‘순위’의 개념을 다른 각도로 살펴볼 필요가 있다. 1등인 직원은 자신보다 높은 점수를 가진 직원이 없다. 2등인 직원은 자신보다 높은 점수를 가진 직원이 1명 있는 셈이다.

그렇다면 5등인 직원은 어떨까 자신보다 높은 점수를 가진 직원이 4명 있는 것이다. 우리가 발견한 이 일정한 규칙을 기반으로 쿼리를 작성하면 된다. 다시 말하면 사원 테이블을 기준으로 점수가 더 큰 직원을 찾은 다음, 찾은 직원 수에 1을 더하면 순위가 나온다.



SELECT a.empno, a.sal
, COUNT(b.empno) + 1 rk
FROM emp a
, emp b
WHERE a.sal < b.sal(+)
GROUP BY a.empno, a.sal
ORDER BY rk
;



column_img_2232.jpg

사원 테이블을 두 번 사용했다. a는 기준이 되는 테이블이고, b는 비교 대상 테이블이다. 기준테이블의 급여보다 큰 급여를 받는 직원을 비교대상 테이블에서 검색한다. 이때 1등인 직원보다 급여가 많은 직원은 없기 때문에 1등인 직원도 나오게 하기 위해 아우터 조인을 사용했다. 그리고 기준테이블의 사원번호를 기준으로 그룹핑하여 건수를 구한다. 건수조인, 그룹바이, 카운트 등을 이용했다.



[질문 33] 그룹별 순위도 분석함수 없이 구할 수 있을까

앞선 [질문 33] 의 풀이를 약간만 개선하면 문제를 해결할 수 있다.



SELECT a.deptno, a.empno, a.sal
, COUNT(b.empno) + 1 rk
FROM emp a
, emp b
WHERE a.deptno = b.deptno(+)
AND a.sal < b.sal(+)
GROUP BY a.deptno, a.empno, a.sal
ORDER BY deptno, rk
;



column_img_2233.jpg

기준급여보다 높은 급여를 받는 직원을 찾는 것은 동일하다. 여기에 같은 부서에서만 찾는다는 조건만 추가하면 된다.



[질문 34] DENSE_RANK 와 ROW_NUMBER 도 분석함수 없이 구해 보자.

-- DENSE_RANK --
SELECT a.empno, a.sal
, COUNT(DISTINCT b.sal) + 1 dr
FROM emp a
, emp b
WHERE a.sal < b.sal(+)
GROUP BY a.empno, a.sal
ORDER BY dr
;



-- ROW_NUMBER --
SELECT empno, sal
, ROWNUM rn
FROM (SELECT empno, sal
FROM emp
ORDER BY sal DESC
)
;



정리하며

분석함수 중 순위를 구하는 함수를 배웠다.
- RANK : 일반적인 순위이다. 공동 순위가 있을 경우 다음 순위를 건너뛴다.
- DENSE_RANK : 공동순위와 관계없이 연속된 순위를 부여한다.
- ROW_NUMBER : 공동순위 없이 일련번호를 부여한다.

분석함수 없이 순위를 구하는 방법을 배웠다.
- 자신의 급여보다 큰 직원을 검색하여 검색된 수에 1을 더하면 순위가 된다.
- Self Join, Outer Join, Group By Count가 사용된다.

다음 회에도 다양한 분석함수에 대해 소개하도록 하겠다. (다음 회에 계속)