ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [PostgreSQL] window function
    DB/PostgreSQL 2017. 2. 22. 15:31

    window function은 aggregate function처럼 어떤 계산을 도와줍니다. 하지만 aggregate function의 결과가 하나의 row로 보여진다면, 이 window function은 row마다 결과를 보여줍니다. 결과적으로 동작이 조금 달라서 이름을 다르게 지었다고 보면 됩니다. 실제로 PostgreSQL에서 Aggregate function을 만들고, 그녀석을 window function처럼 사용할 수 있습니다.

    예제

    avg()

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

    위의 sql 을 해석하면 아래와 같습니다.

    • empsalary table 에서 salary에 대한 avg() 를 구함. 그런데 avg() 는 "depname 칼럼의 값"을 구분(partition)해서 구함.
    • 주의할 점은 OVER가 들어가야 window function으로 인식. 그렇지 않으면 그냥 aggregation이 됨.

    만약 group by 를 사용한다면, 아래처럼 바꿀 수 있을 것입니다.

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

    결과

    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)

    rank()

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

    empsalary table에서 depname의 값별로 salary에 대한 rank 를 구합니다.

    ORDER BY

     이 window function에서 OVER부분에 들어가는 ORDER BY는 역할이 좀 다릅니다. 이 partition 안에서 row들의 집합을 window frame이라고 하는데, 많은 window function이 partition전체에서 동작하지 않고 이 window frame의 row 들에서 동작합니다.

    ORDER BY가 없으면 기본적으로 window frame은 partition의 모든 row가 됩니다. 그런데 ORDER BY가 OVER에 들어가면 ORDER BY한 column 각각의 row가 window frame이 됩니다. 이 때 중복된 값은 같은 frame이 됩니다.

    예제

    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)


    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)

    2개의 column 값 더해서 새로운 값

    이번에는 2개의 rank 를 구하고, 이것을 더해 보겠습니다.

    SELECT t.code, rank+rank_2 AS sum_rank
    from
    (
     SELECT code, market, 
      rank() OVER (PARTITION BY market ORDER BY price DESC) AS rank,
      rank() OVER (ORDER BY price DESC) AS rank_2
     FROM test_market
    ) AS t 
    ORDER BY sum_rank DESC;

    test_market이라는 table에서 2개의 rank를 구하는데, 1개는 market값 당 price로 내림차순 정렬하여 rank를 구하고, 다른 한 개(rank_2)는 그냥 price 당 내림차순 정렬해서 구합니다. 이렇게 나온 순위(rank)를 더해서 sum_rank 를 구하고 내림차순을 한 쿼리입니다.

    partition by

    partition by는 위의 예제에서 확인한 것처럼 PARTITION BY로 정의하는 목록이 같은 그룹인지, 다른 그룹인지 구분하는 기준으로 사용되고, 같은 그룹 내에서는 현재 로우 값도 포함해서 계산된 윈도우 함수의 결과 값을 공유합니다.

    응용

    여러 개의 윈도우 함수를 함께 쓸 때는 각각 OVER 절을 사용해야하는데, 이 때 지정할 원도우 프래임이 복잡하다면, 중복 입력해야하는 이슈가 발생해 오류가 발생할 가능성이 커집니다. 이 문제점을 줄이기 위해서 WINDOW절을 이용합니다

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

    함수 종류

    아래는 윈도우 함수의 리스트입니다.

    1. row_number
    2. rank
    3. dense_rank
    4. percent_rank
    5. cume_dist
    6. ntile
    7. lag
    8. lead
    9. first_value
    10. last_value
    11. nth_value


    더 자세한 내용을 아래 링크에서 확인할 수 있습니다.

    공식문서: https://www.postgresql.org/docs/9.5/static/tutorial-window.html

    번역문서: http://postgresql.kr/docs/9.5/tutorial-window.html

    댓글