2024년 12월에 solvesql에서 ‘Advent of SQL 2024’라는 SQL 어드벤트 챌린지를 이벤트 삼아 진행했다. 어드벤트 캘린더에서 아이디어를 착안해 SQL 문제를 매일매일 풀어본다니, 아이디어가 너무 좋다고 생각했다. 또, 그냥 공부하면 하기 싫은데, 이렇게 이벤트성으로 + 완수하면 뱃지도 주고(물욕) + 한문제씩만 푼다면 부담도 없을 것 같고, 안풀려도 심도있게 고민해볼 수 있을 것 같고 + 이벤트에 참여한 다른 사람들도 있을 것이고 여러모로 흥미로운 이벤트였다.
제목을 거창하게 썼지만, SQL 쿼리 연습문제를 풀어보며 실무에서 하는 부분이 문제에 많이 녹아있다는 것을 느꼈다. 기계적으로 쿼리를 작성하는 것이 아니라, 문제에 녹아있는 실무적인 부분을 상상하는 습관을 잊지말자는 다짐 차원에서 제목을 붙여보았다.
SQL 문제를 풀며 어떤 부분을 느꼈냐면, 아래 두 가지가 있다.
1. 데이터에서 전처리해야 하는 문제를 맛볼 수 있다.
(Advent of SQL 2024 중 241209일자 문제 / 난이도3 / 게임 평점 예측하기 1 을 풀며)
games란 테이블에는 발매된 게임 정보가 들어있다. games 테이블에서는 게임 별 그 게임에 대한 평론가 평점과 사용자 평점 데이터도 함께 있다. 문제에서는 ‘누락된 평점 정보’가 있기 때문에 이 정보를 채우는 쿼리를 작성해야 한다고 서술한다.
→ 첫 번째 생각 : 와 나 일할 때도 이런 일 많지
음음, 업무할 때 누락된 정보 없었던 적이 없지. (제조회사 특성 상, 누락된 데이터는 예상할 수 없는 범위에서 아주 많다.)
→ 두 번째 생각 : 과연 누락된 정보만 있을까?
- 잘못 입력된 정보 : 1원인 단가를 보았는가
- 현장 상황 때문에 일반적인 패턴과 달리 수정되어 입력된 정보인데 입력한 사람이 아니면 ‘그’ 현장 상황이 무엇인지 몰라 아무도 해석을 못하는 정보 : 분명히 A품명으로 등록되었는데, 당시 A품명 재고가 떨어져 A’품명으로 생산된 다른 제품이 나갔으나 품명은 고쳐지지 않고, 매출 데이터도 울고 재고 데이터도 울고 나도 울고
- 값은 다 입력이 되었으나 형식이 틀린 정보 : 지원팀에 입력해 주문 내역을 등록할 때는 00이란 컬럼에 ‘업체/담당자/비고’ 순으로 기록해주세요! 라고 했으나, ‘업체담당자/비고’로 등록된 정보
이런 경우는 아주아주 흔하다. 여기서 많은 분석가들이 치명적이라 잡아야 하는 데이터라면 데이터가 잘못 입력된 이상 범위를 설정하고 잡는 프로세스를 수립해야겠고, 아니라면 어디까지 포기하고 넘어갈 것인지, 입력부터 잡을 수는 없어도 전처리로 잡을 수 있는지 등을 매일매일 고민하고 있을 것이다.
→ 세 번째 생각 : 누락된 정보를 채우는 방법으로 어떤 것을 선택할 것인가?
이 문제에서는 누락된 정보를 채우는 방법으로 누락된 게임과 같은 장르인 게임들의 평점 평균을 계산하여 삽입하는 방법을 선택한다.
그래서 쿼리도 COALESCE(표현식1, 표현식2)를 사용한다. games 테이블에 누락된 평점 정보(표현식1)가 있으면, 누락된 게임과 같은 장르인 게임들의 평점 평균(표현식2)를 출력하도록 하기 위해서이다. 번외로, SQLP 시험을 볼 때 ISNULL/NVL/NULLIF/COALESCE 구분이 단골 문제처럼 나오는데, SQL 쿼리를 짤 때 아주 많이 사용하고 있다.
업무를 할 때는 누락된 정보를 평균으로 채울 것인지 / 0으로 채울 것인지 / 삭제할 것인지 고민을 해본다. 고민의 과정에서 각 값들을 넣었을 때 결과물을 비교해본다. 나는 아직은 데이터는 많은 가공을 거치지 않는 것을 선호해 보통은 0으로 두는 편이다. 하지만 아직까지는 평균을 했을 때와 0으로 했을 때는 결과값에 큰 차이가 생길 정도는 아니었다.
→ 기타 생각 : 지원이 되지 않는 기능을 리마인드 할 수 있다.
업무에서는 오라클을 사용하고 있기 때문에 습관적으로 NVL을 사용했는데, 문제에서는 MySQL이라 NVL이 동작하지 않았다. 덕분에 COALESCE는 두 개 이상의 인수를 사용하며, NVL은 MySQL에서는 지원이 되지 않는 것을 다시 배웠다.
2. 쿼리 개선에 대한 고민을 맛볼 수 있다.
(Advent of SQL 2024 중 241210일자 문제 / 난이도2 / 최대값을 가진 행 찾기 를 풀며)
테이블에서 x컬럼 중 최대값의 ID와 y컬럼 중 최대값의 ID를 출력하는 것이 문제였다. 이 문제에 대해서 쿼리는 짧게 작성될 수도 있고, 길게 작성될 수도 있다.
예를 들어, 같은 문제에 대해 아래처럼 다양한 방법으로 쿼리가 짜일 수 있다.
1) 조건절에서 MAX값을 산출하기
SELECT id
FROM 테이블
WHERE x = (SELECT MAX(x) FROM 테이블)
OR y = (SELECT MAX(y) FROM 테이블)
ORDER BY id
2) MAX 값을 따로 구하고, JOIN 또는 UNION 하기
/* JOIN 을 할 때 */
SELECT id
FROM 테이블
LEFT JOIN (MAX(x)를 구하는 쿼리)
LEFT JOIN (MAX(y)를 구하는 쿼리)
ORDER BY id
/* UNION 을 할 때 */
SELECT id
FROM (MAX(X)를 구하는 쿼리)
UNION
SELECT id
FROM (MAX(y)를 구하는 쿼리)
ORDER BY id
3) MAX 값을 따로 구하는 부분은 CTE 구문으로 처리하기
WITH Max_xy AS(
SELECT
MAX(x) AS max_x,
MAX(y) AS max_y
FROM 테이블
)
SELECT id
FROM points, Max_xy
WHERE x = max_x, y = max_y
ORDERY BY id
→ 생각 : 일단 급하다는 요청을 받아서, 0번처럼 쿼리를 작성했는데, 다시 보니 그냥 돌아가게만 갈겨둔 쿼리같네?
그렇게 되면 아래처럼 어떤 쿼리가 좋을지 고민보따리가 열린다. 그 고민의 방향은 크게 두 가지로 나눠지는 것 같다. 첫 번째, 깔끔한 쿼리가 맞는지. 두 번째, 처음 보는 사람이 봐도 명확하게 짜인 쿼리가 맞는지.
첫 번째는 쿼리의 성능에 대한 부분이고, 두 번째는 쿼리를 작성한 사람이 언제까지고 그 업무만 담당할 수는 없으니 업무를 길게 봤을 때 고민이 되는 부분이다.
서브쿼리가 나을까? JOIN을 쓰면 출력이 느려질텐데 이 정도 데이터양에서는 괜찮을까? JOIN보다 UNION이 낫나? 본 쿼리를 길게 가져가면 수정사항이 생길 때 더 복잡해지니까 이 부분은 CTE로 뺄까? 불필요한 쿼리가 들어가 있지는 않나? 내가 아니라 다른 사람이 봐도 이 쿼리를 고칠 수 있을까? 주석은 적절히 들어가 있나?
결론
문제들을 풀며 내가 업무에서 SQL을 작성하고 수정하며 쓰는 쿼리나 실무에서 자주 행해야 하는 것들이 나와 있었다. 취준 때는 단순히 코테를 위해 문제를 풀었는데, 실무를 반영하여 문제를 짜기 위해 많은 사람들의 고민이 들어갔겠구나 싶었다. 또 문제 만으로도 SQL을 사용한 실무 분석 연습을 할 수 있겠다 싶었다. (이미 경험해봤으니 실무처럼 보이는거지, 돌아가면 그저 SQL 코테 연습문제로 보였을 듯)
아무튼, 가끔 SQL 문제로 공부를 하게 되는데, 단순한 문제로만 보지 말자고 다시금 되새기게 되었다!
댓글