문제
SQLite
내가 작성한 정답 1 - row_number()
: row_number로 순번을 구한 후 중간순번에 해당하는 중간값을 가져왔다.
with a as ( select case
when strftime ('%m', measured_at) in ('03', '04', '05') then 'spring'
when strftime ('%m', measured_at) in ('06', '07', '08') then 'summer'
when strftime ('%m', measured_at) in ('09', '10', '11') then 'autumn'
else 'winter' end season, pm10
from measurements ),
b as ( select season, count(*) cnt, round(avg(pm10),2) pm10_average
from a
group by season
order by pm10 )
select b.season, c.pm10 pm10_median, b.pm10_average
from b b
join (select row_number() over(partition by a.season order by a.pm10) ranking,a.season,a.pm10
from b b join a a on b.season = a.season) c
on b.season = c.season and (cnt+1)/2 = ranking
ROW_NUMBER() OVER
: 결과 집합의 각 행에 대해 고유한 순번을 부여한다. 이 함수는 주로 데이터의 순서를 매기거나 특정 조건에 따라 그룹화된 데이터 내에서 행 번호를 생성하는 데 사용된다.
- PARTITION BY: 이 절은 결과 집합을 특정 열의 값에 따라 그룹화한다. 각 그룹 내에서 ROW_NUMBER()가 다시 1부터 시작한다. 이 절은 선택 사항!
- ORDER BY: 이 절은 각 그룹 내에서 행 번호를 매길 순서를 지정합니다. 이 절은 필수!
ROW_NUMBER() OVER (PARTITION BY column_name ORDER BY column_name)
// 아래의 쿼리는 각 부서(department_id) 내에서 직원(employee_id)에 대해 순번을 매긴다.
// 각 부서의 첫 번째 직원은 row_num이 1이 되고, 두 번째 직원은 2가 된다.
SELECT
employee_id,
department_id,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY employee_id) AS row_num
FROM
employees;
- ORDER BY 절에서 지정한 열의 값이 동일한 여러 행이 존재한다면, ROW_NUMBER()는 이들 행에 대해 각각 다른 번호를 부여한다. 즉, 동일한 값이 있는 행이라도 순서에 따라 번호가 다르게 매겨진다.
내가 작성한 정답 2 - PERCENT_RANK()
: percent_rank()로 퍼센트 비율을 구한 후 중간퍼센트인 0.5에 해당하는 중간값을 가져왔다.
이 때 계절별로 데이터가 홀수개라면 0.5가 정확히 나올 수 없으므로 ceiling으로 올림을 해서 가져온다.
올림을 할 때 소수점 첫째자리까지 나오면 0.55같은 경우는 올림이 제대로 되지 않고 소수점 둘째짜리까지 나오면 데이터가 홀수인 것들은 정확히 0.5가 될 수 없으므로 or 조건으로 두가지 경우를 가져와 max로 최대값을 출력한다.
with a as ( select case
when strftime ('%m', measured_at) in ('03', '04', '05') then 'spring'
when strftime ('%m', measured_at) in ('06', '07', '08') then 'summer'
when strftime ('%m', measured_at) in ('09', '10', '11') then 'autumn'
else 'winter' end season, pm10
from measurements ),
b as ( select season, round(avg(pm10),2) pm10_average
from a
group by season
order by pm10 )
select b.season, max(c.pm10) pm10_median, b.pm10_average
from b b
join (select percent_rank() over(partition by a.season order by a.pm10) ranking,a.season,a.pm10
from b b join a a on b.season = a.season) c
on b.season = c.season
where (ceiling(c.ranking*100))*0.01 = 0.5
or (ceiling(c.ranking*10))*0.1 = 0.5
group by b.season
PERCENT_RANK()
: 함수는 SQL에서 사용되는 윈도우 함수 중 하나로, 특정 행의 순위를 백분율로 계산한다. 이 함수는 주어진 집합 내에서 각 행의 상대적 위치를 나타내며, 0에서 1 사이의 값을 반환한다.
PERCENT_RANK() OVER (PARTITION BY partition_expression ORDER BY order_expression)
다른 사람들이 작성한 정답
SELECT
CASE
WHEN substr(measured_at, 6, 2) IN ('03', '04', '05') THEN 'spring'
WHEN substr(measured_at, 6, 2) IN ('06', '07', '08') THEN 'summer'
WHEN substr(measured_at, 6, 2) IN ('09', '10', '11') THEN 'autumn'
ELSE 'winter'
END season,
median(pm10) pm10_median,
round(avg(pm10), 2) pm10_average
FROM measurements
GROUP BY 1;
median()함수를 사용한다면 중간값을 쉽게 구할 수 있다.
Oracle
SELECT
season,
ROUND(AVG(pm10), 2) AS pm10_average,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY pm10) AS pm10_median
FROM (
SELECT
CASE
WHEN TO_CHAR(TO_DATE(date, 'YYYY-MM-DD'), 'MM') BETWEEN '03' AND '05' THEN 'spring'
WHEN TO_CHAR(TO_DATE(date, 'YYYY-MM-DD'), 'MM') BETWEEN '06' AND '08' THEN 'summer'
WHEN TO_CHAR(TO_DATE(date, 'YYYY-MM-DD'), 'MM') BETWEEN '09' AND '11' THEN 'autumn'
ELSE 'winter'
END AS season,
pm10
FROM measurements
)
GROUP BY season;
oracle에서는
PERCENTILE_CONT(0.5) WITHIN GROUP을 사용하여 중앙값을 쉽게 구할 수 있다.
median()함수도 바로 사용가능하다
Share article