Today I Learned (TIL)/SQL

[TIL] SQL 챌린지 - 5회차

신짜린 2024. 7. 26. 19:34

1. 윈도우 구문

 Postrgres SQL 등 엄격한 ANSI SQL을 제공하는 경우 SELF JOIN 필수이다.

 윈도우 구문은 이동평균, 누적 합계, 순위 계산이 가능하여 일종의 서브쿼리 역할을 한다. 윈도우 구문을 사용하면 가독성이 올라간다.

-- 일반적인 구문
SELECT 집계함수() OVER ( [PARTITION BY {컬럼명1}], [ORDER BY {컬럼명2}]) 
FROM {테이블};

 

2. 윈도우 구문 함수

- ORDER BY 구문

 함수 내에서 데이터를 정렬하는 데 사용된다. 일반적인 ORDER BY 구문과 달리 윈도우 함수가 적용되는 범위 내에서만 정렬된다.

-- 전체 직원을 대상으로 월급을 많이 받는 순위 매기기
SELECT 
    employee_id,
    department_id,
    salary,
    RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM 
    hr_employees;

 

- PARTITION BY 구문

 데이터를 특정 기준으로 그룹화하여 별도로 윈도우 함수를 적용하는 방법이다. GROUP BY 구문은 전체 데이터를 기준으로 그룹화 하는 것과 대조적이다.

-- 부서별로 누가 돈을 평균적으로 잘 받는지 확인하기
SELECT 
    employee_id,
    department_id,
    salary,
    AVG(salary) OVER (PARTITION BY department_id) AS avg_salary_by_dept
FROM 
    hr_employees
ORDER BY avg_salary_by_dept;

 

- ORDER BY + PARTITION BY

-- 부서별로 나눴을 때 월급 받는 순위 매겨보기
SELECT 
    employee_id,
    department_id,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank_by_dept
FROM 
    hr_employees
ORDER BY EMPLOYEE_ID;

 

3. 윈도우 함수

- 집계 함수

함수 설명
SUM() 더하기
AVG() 평균
COUNT() 갯수
MIN() 최소
MAX() 최대

 

- 순위 함수

함수 설명
RANK() 동인한 값에 동일한 순위를 매기고 다음 순위는 건너 뜀
DENSE_RANK() 동일한 값에 동일한 순위를 매기지만 다음 순위를 건너뛰지 않음
ROW_NUMBER() 동일한 값이 있어도 유일한 순위를 부여
PERCENT_RANK() 백분율 순위 계산
NTILE(N) 파티션 내의 행을 N 등분
(기본적으로 ORDER BY가 있어야 작동)
-- NTILE 함수
SELECT EMPLOYEE_ID , DEPARTMENT_ID ,salary,
NTILE(5) OVER 
(PARTITION BY DEPARTMENT_ID ORDER BY salary desc) AS ntile2
FROM HR_EMPLOYEES HE 
ORDER BY EMPLOYEE_ID ASC ;
-- 모든 직원을 대상으로 급여 백분위 IQR로 상한이상치, 정상범위, 하한이상치 찾아보기
WITH salary_ranks AS (
    SELECT 
        salary,
        PERCENT_RANK() OVER (ORDER BY salary) AS pct_rank
    FROM hr_employees
),
quartiles AS (
    SELECT 
        MIN(CASE WHEN pct_rank >= 0.25 THEN salary END) AS Q1,
        MIN(CASE WHEN pct_rank >= 0.75 THEN salary END) AS Q3
    FROM salary_ranks
),
iqr_calc AS (
    SELECT 
        Q1,
        Q3,
        (Q3 - Q1) AS IQR,
        (Q1 - 1.5 * (Q3 - Q1)) AS lower_bound,
        (Q3 + 1.5 * (Q3 - Q1)) AS upper_bound
    FROM quartiles
)
-- 2. 이상치 판별
SELECT
    e.first_name, 
    e.last_name,
    e.salary
    ,
    CASE 
        WHEN e.salary < iqr_calc.lower_bound THEN 'LOWER_OUTLIER'
        WHEN e.salary > iqr_calc.upper_bound THEN 'UPPER_OUTLIER'
        ELSE 'ORDINARY' 
    END AS outlier_chk
FROM 
    hr_employees e, IQR_CALC;

 

- 값 반환 함수

 행을 기준으로 이전, 이후 값 혹은 윈도우 내 첫번째, 마지막 값을 가져와야할 수 있다.

함수 설명
FIRST_VALUE() 윈도우 프레임 내 첫 번째 값 반환
LAST_VALUE() 윈도우 프레임 내 마지막 값 반환
LAG() 윈도우 프레임 내 이전 행 값 반환
LEAD() 윈도우 프레임 내 다음 행 값 반환
-- 부서별 최고 연봉자 찾아보기
SELECT 
    employee_id,
    department_id,
    salary,
    FIRST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary DESC) AS highest_salary_in_dept
FROM 
    hr_employees
ORDER BY employee_id ASC;
-----------------------------------------------------------------------------------------------
-- 사번을 기준으로 선배의 월급 정보 가져오기
SELECT 
    employee_id,
    manager_id,
    salary,
    LAG(salary, 1) OVER (ORDER BY EMPLOYEE_ID ASC) AS previous_salary
FROM 
    HR_employees
ORDER BY employee_idASC;

 

4. 윈도우 구문2 - 범위 지정

- Frame Specification

 프레임은 윈도우의 하위 집합이다.

구문 설명
UNBOUNDED PRECEDING 윈도우 내 첫 번째 부터(기본 옵션)
UNBOUNDED FOLLOWING 윈도우 내 마지막 행 까지(기본 옵션)
CURRENT ROW 현재행 포함
N PRECEDING 현재 행에서 이전 N행 포함
N FOLLOWING
현재 행에서 다음 N행 포함
-- 일반적인 구문
SELECT 집계함수() OVER ( [PARTITION BY {컬럼명1}], [ORDER BY {컬럼명2}] [ROWS|RANGE {범위}]) 
FROM {테이블};
-- ROWS: 물리적인 행 단위 프레임 설정
-- BETWEEN: 논리적인 값을 범위로 지정
-----------------------------------------------------------------------------------------------
-- 기본 구문
SELECT 
    employee_id,
    department_id,
    salary,
    ROUND(AVG(salary) OVER (PARTITION BY department_id)) AS avg_salary_by_dept
FROM 
    hr_employees
ORDER BY EMPLOYEE_ID ASC
-----------------------------------------------------------------------------------------------
-- RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
SELECT 
    employee_id,
    department_id,
    salary,
    ROUND(AVG(salary) OVER (PARTITION BY department_id RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS avg_salary_by_dept
FROM 
    hr_employees
ORDER BY EMPLOYEE_ID ASC;
-- 직원 입사순으로 누적 월급 구하기(부서별이 아닌 전체를 기준으로)
-- SUM() + ORDER BY
SELECT 
    employee_id,
    department_id,
    salary,
    ROUND(SUM(salary) OVER (ORDER BY EMPLOYEE_ID ASC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)) AS avg_salary_by_dept
FROM 
    hr_employees
ORDER BY EMPLOYEE_ID ASC
-----------------------------------------------------------------------------------------------
-- 직원 입사순으로 부서별 누적 월급 구하기
-- SUM() + ORDER BY + PARTITION BY
SELECT 
    employee_id,
    department_id,
    salary,
    ROUND(SUM(salary) OVER (PARTITION BY DEPARTMENT_ID ORDER BY EMPLOYEE_ID ASC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)) AS avg_salary_by_dept
FROM 
    hr_employees
ORDER BY EMPLOYEE_ID ASC

'Today I Learned (TIL) > SQL' 카테고리의 다른 글

[TIL] SQL 베이직 - 5회차  (0) 2024.07.29
[TIL] SQL 챌린지 - 6회차  (0) 2024.07.29
[TIL] SQL 베이직 - 4회차  (0) 2024.07.25
[TIL] SQL 베이직 - 3회차  (0) 2024.07.25
[TIL] SQL 챌린지 - 4회차  (0) 2024.07.24