SQL 코드카타 176번
이 문제에서 구해야 할 것들은
처음에는 하나의 Project는 end_date와 start_date가 같은 task끼리 연결되어 있다는 점을 이용해 join으로 해결해 보려고 이것저것 시도해 봤는데, 이렇게 되면 프로젝트 안에 들어있는 task의 수가 늘어남에 따라 join을 그에 맞춰서 계속 해줘야 하기 때문에 일반화된 답을 구할 수 없다.
먼저 Projects 테이블을 start_date 순으로 오름차순 정렬하고, row_number 함수를 써서 각 행별로 번호를 부여해 준다.
SELECT Row_number()
OVER(
ORDER BY start_date) AS num,
task_id,
start_date,
end_date
FROM projects p1
ORDER BY start_date

문제의 논리에 따르면 종료일과 시작일이 겹치는 1, 24, 2, 23번이 하나의 프로젝트로 묶이게 되는데, row_number()의 결과도 1씩 증가하고 end_date도 1씩 증가한다는 속성을 이용해 Project끼리 구분할 수 있는 구분값을 만들어 준다.
SELECT task_id,
start_date,
end_date,
end_date - Row_number()
OVER(
ORDER BY start_date) AS project_no
FROM projects p1
ORDER BY start_date

이제 이미지에서 보이는 것처럼 같은 project_no를 가진 것들끼리 그루핑을 해 주면 날짜의 최소값, 최대값, 그리고 프로젝트 기간을 구할 수 있다. project_no를 컬럼처럼 사용하기 위해 위 쿼리를 인라인뷰 서브쿼리로 처리하고, min(start_date)와 max(end_date)를 구한다.
SELECT project_no,
Min(start_date),
Max(end_date)
FROM (SELECT task_id,
start_date,
end_date,
end_date - Row_number()
OVER(
ORDER BY start_date) AS project_no
FROM projects p1) a
GROUP BY 1
출력해야 할 답에 project_no는 들어가면 안 되므로(위 쿼리에서 project_no를 바로 빼면 group by를 할 수 없으므로), 위 쿼리를 다시 한 번 인라인뷰 서브쿼리 처리하고 문제의 조건대로 order by를 맞춰주면
SELECT project_start,
project_end
FROM (SELECT project_no,
Min(start_date) AS project_start,
Max(end_date) AS project_end
FROM (SELECT task_id,
start_date,
end_date,
end_date - Row_number()
OVER(
ORDER BY start_date) AS project_no
FROM projects p1) a
GROUP BY 1) b
ORDER BY project_end - project_start ASC,
project_start ASC

위와 같이 정답이 나온다.
연속된 날짜나 숫자들을 같은 그룹으로 처리해서 해결해야 하는 문제가 있을 때는
SQL 코드카타의 난이도는 다시 올라가고 있지만 차분히 풀다 보면 이해가 안 가는 문제는 없다. 한 번도 접하지 못한 엄청난 함수나 복잡한 풀이법이 등장했던 적도 없고. 첫 번째 시도가 실패했을 때 멘붕이 오지만 않게 멘탈 관리 잘 하자.