3.5. 윈도우 함수

윈도우 함수는 테이블에서 로우 집합을 대상으로 계산하는 함수다. 그 로우 집합은 한 로우에 대해서 어떠한 방식으로 관계된 로우들이다. 이 함수는 로우 집합 단위로 계산한다는 점에서 집계 함수와 비슷하다. 집계 함수는 해당 되는 로우 집합에 대해서 하나의 로우로 그 결과물을 보여주지만, 윈도우 함수는 각 로우 단위로 그 처리결과를 출력한다. 숨은 이야기를 하자면, 윈도우 함수는 쿼리 결과의 현재 로우 뿐만 아니라 더 많은 것을 접근할 수 있다.

다음 예제는 각 부서별 평균 임금과 각 직원별 급여를 비교하는 것이다:

SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;

  depname  | empno | salary |          avg          
-----------+-------+--------+-----------------------
 develop   |    11 |   5200 | 5020.0000000000000000
 develop   |     7 |   4200 | 5020.0000000000000000
 develop   |     9 |   4500 | 5020.0000000000000000
 develop   |     8 |   6000 | 5020.0000000000000000
 develop   |    10 |   5200 | 5020.0000000000000000
 personnel |     5 |   3500 | 3700.0000000000000000
 personnel |     2 |   3900 | 3700.0000000000000000
 sales     |     3 |   4800 | 4866.6666666666666667
 sales     |     1 |   5000 | 4866.6666666666666667
 sales     |     4 |   4800 | 4866.6666666666666667
(10 rows)

첫번째 칼럼부터 세번째 칼럼까지는 empsalary 테이블의 자료 그대로다. 여기서 하나의 로우는 실재 테이블 자료의 실재 로우다. 네번째 칼럼은 depname 칼럼 값을 기준으로 그룹화된 자료의 평균값을 각 로우마다 보여준다. (이 예제에서 보이는 avg 함수는 집계 함수와 그 사용법이 완벽하게 같은데, 뒤에 오는 OVER 절로 판단해서 윈도우 함수로 처리된다.)

윈도우 함수 뒤에는 항상 OVER 절을 사용한다. 이 규칙은 이 함수가 윈도우 함수로 처리할 것인지, 그렇지 않을 것인지를 구분하는 문법상 규칙이다. OVER 절에는 윈도우 함수의 계산 대상이 되는 로우들을 집합화하는 방법을 정의한다. PARTITION BY로 정의하는 목록은 같은 그룹인지, 다른 그룹인지 구분하는 기준으로 사용되고, 같은 그룹 내에서는 현재 로우 값도 포함해서 계산 된 윈도우 함수의 결과값을 공유한다.

또한 OVER 절로 집합화 하는 로우들의 정렬 순서를 ORDER BY 절을 사용해서 지정할 수 있다. (The window ORDER BY does not even have to match the order in which the rows are output.) 다음은 그 예제다:

SELECT depname, empno, salary,
       rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;

  depname  | empno | salary | rank 
-----------+-------+--------+------
 develop   |     8 |   6000 |    1
 develop   |    10 |   5200 |    2
 develop   |    11 |   5200 |    2
 develop   |     9 |   4500 |    4
 develop   |     7 |   4200 |    5
 personnel |     2 |   3900 |    1
 personnel |     5 |   3500 |    2
 sales     |     1 |   5000 |    1
 sales     |     4 |   4800 |    2
 sales     |     3 |   4800 |    2
(10 rows)

위에서 보는 바와 같이, 이 결과는 부서명을 기준으로 급여가 많은 자료부터 차례로 출력하는데, 이 때 rank 함수를 사용하여 그 순위를 보여주는 것이다. rank 함수의 입력 매개 변수는 필요없다. 다음에 오는 OVER 절에서 이 함수에서 처리해야할 모든 입력 정보들이 다 정의되었기 때문이다.

윈도우 함수의 계산 대상이 대는 로우는 FROM 다음부터 WHERE, GROUP BY, HAVING 절로 각 조건에 맞게 걸러진 가상 테이블의 각 로우다. 예를 들어, WHERE 조건절을 통해서 이미 걸러져 버린 로우에 대해서는 원도우 함수의 계산 대상이 되지 않는다. 하나의 쿼리에 여러개의 윈도우 함수를 사용할 수 있다. 이 함수들은 각각의 OVER 절을 사용하여 각각 자료를 집합화 한다. 하지만, 이 집합화 대상이 대는 가상 테이블은 같아야한다.

이미 살펴 보았듯이, ORDER BY 절이 필요 없는 경우라면, 생략할 수 있다. 이와 마찬가지로 PARTITION BY 절도 생략할 수 있으며, 이것이 생략된다면, 윈도우 함수가 처리하는 같은 집합은 처리 결과 모든 로우의 집합이 된다.

윈도우 함수와 관련된 또 하나 중요한 사항은 다음과 같다: 윈도우 프래임 window frame이라는 개념인데, 구분 대상이 되는 로우 집함들 가운데, 현재 윈도우 함수가 처리 하는 로우 집합을 뜻한다. 많은 윈도우 함수들이 이 윈도우 프래임 단위로 계산을 한다. (물론 전부는 아니다) 초기값으로 ORDER BY 절을 사용하면, 윈도우 프래임은 해당 정렬 방법으로 정렬 해서 순차적으로 현재 로우까지가 된다. ORDER BY 절이 빠지면, 그 구분되는 집합들의 모든 로우가 윈도우 함수의 계산 대상이 된다. [4] 다음은 sum 윈도우 함수를 사용한 예다:

SELECT salary, sum(salary) OVER () FROM empsalary;
 salary |  sum  
--------+-------
   5200 | 47100
   5000 | 47100
   3500 | 47100
   4800 | 47100
   3900 | 47100
   4200 | 47100
   4500 | 47100
   4800 | 47100
   6000 | 47100
   5200 | 47100
(10 rows)

위에서 처럼, OVER 절에 ORDER BY 정의를 하지 않았고, PARTITION BY 절을 사용하지 않아서 가상 테이블은 실재 테이블의 전체 자료가 된다. 즉, 모든 로우에는 같은 윈도우 함수의 결과값이 출력된다. 하지만 ORDER BY 정의를 하게 되면 다음과 같이 결과는 전혀 다르게 나온다.

SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
 salary |  sum  
--------+-------
   3500 |  3500
   3900 |  7400
   4200 | 11600
   4500 | 16100
   4800 | 25700
   4800 | 25700
   5000 | 30700
   5200 | 41100
   5200 | 41100
   6000 | 47100
(10 rows)

윈도우 함수는 현재 로우를 포함 해서, 급여가 제일 적은 것부터 차례대로 함수 결과를 그때 그때 출력한다. (여기서 주의할 사항은 윈도우 함수는 현재 로우의 자료를 포함해서 계산 한다는 것이다.)

윈도우 함수는 SELECT 항목 영역 안에서와, ORDER BY 절에서만 사용할 수 있다. 그외 GROUP BY 절이나, HAVING, WHERE 절에서는 사용할 수 없다. 이는 논리적으로 이 함수의 처리 대상이 되는 입력 매개 변수값이 이미 결정이 나야하기 때문이다. 이와 같은 이유로, 윈도우 함수의 입력 매개 변수로 집계 함수의 결과를 사용 할 수는 있지만, 그 반대는 불가능 하다.

윈도우 함수를 사용한 결과에 대한 검색 조건이나, 재 집계가 필요하다면, 서브 쿼리를 사용할 수 있다. 아래 예제 참고:

SELECT depname, empno, salary, enroll_date
FROM
  (SELECT depname, empno, salary, enroll_date,
          rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
     FROM empsalary
  ) AS ss
WHERE pos < 3;

윗 쿼리는 rank 값이 3보다 작은 자료들만 출력한다.

여러 개의 윈도우 함수를 함께 쓸 때는 각각 OVER 절을 사용해야하는데, 이 때 지정할 원도우 프래임이 복잡하다면, 그것을 계속 중복 입력해야는 것과, 이로 인해 오류를 낼 가능성 커진다. 이 문제점을 줄이기 위해서 WINDOW 절을 이용한다. 다음은 그 예다:

SELECT sum(salary) OVER w, avg(salary) OVER w
  FROM empsalary
  WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);

윈도우 함수에 대한 보다 많은 정보들은 4.2.8절, 9.22절, 7.2.5절, 및 SELECT 설명서 페이지에서 다루고 있다.



[4] 물론, 이 기본 특성을 바꾸기 위해 윈도우 프래임을 바꾸는 옵션도 있지만, 이 자습서의 범위를 벗어나 생략한다. 자세한 내용은 4.2.8절을 참조한다.