조인이 두렵지 않게 되는 그 날까지!
1378. Replace Employee ID With The Unique Identifier
#JOIN
SELECT U.UNIQUE_ID, E.NAME
FROM EMPLOYEES E
LEFT JOIN EMPLOYEEUNI U
ON U.ID = E.ID
1068. Product Sales Analysis I
#JOIN
SELECT p.product_name, s.year, s.price
FROM sales s
JOIN product p ON s.product_id = p.product_id
1581. Customer Who Visited but Did Not Make Any Transactions
#JOIN #ISNULL
SELECT V.CUSTOMER_ID, COUNT(V.VISIT_ID) AS COUNT_NO_TRANS
FROM VISITS V
LEFT JOIN TRANSACTIONS T
ON V.VISIT_ID = T.VISIT_ID
WHERE T.TRANSACTION_ID IS NULL
GROUP BY V.CUSTOMER_ID
197. Rising Temperature
#JOIN #LAG #CROSSJOIN
풀이 방법이 다양해서 하나하나 뜯어보는 재미가 있었던 이번 문제. 시간을 투입하는 만큼 배움도 깊어진다.
[오답]
처음 풀었을 때는 호기롭게 LAG를 이용한 서브쿼리를 짰다가... 날짜 열에 하루라도 공백이 있는 경우 오류가 나서 탈락. 하루 전의 데이터만 비교하겠다는 날짜 조건이 필수로 필요했다.
SELECT ID
FROM (SELECT ID, RECORDDATE, TEMPERATURE,
LAG(TEMPERATURE) OVER (ORDER BY RECORDDATE)
AS PRE_TEMP
FROM WEATHER) W
WHERE TEMPERATURE > PRE_TEMP
[정답 1] 위드 구문 활용하기 (WITH + LAG)
어제 스터디에서 한 분이 현업에서는 서브쿼리를 쓰면 코드가 복잡하고 인식하기 어렵게 되기 때문에 주로 WITH 구문을 활용한다는 말씀을 주셨었는데, 이렇게 바로 나올 줄 몰랐다...
덕분에 더 열심히 익혔다. 확실히 서브쿼리보다 훨씬 보기 좋아서 좋아!
WITH WEATHER_WITH_PRE AS (
SELECT ID, RECORDDATE, TEMPERATURE,
LAG(TEMPERATURE) OVER (ORDER BY RECORDDATE) AS PRE_TEMP,
LAG(RECORDDATE) OVER (ORDER BY RECORDDATE) AS PRE_DATE
FROM WEATHER
)
SELECT ID
FROM WEATHER_WITH_PRE
WHERE TEMPERATURE > PRE_TEMP AND
RECORDDATE = DATE_ADD(PRE_DATE, INTERVAL 1 DAY)
[정답 2] 조인 활용하기 (INNER JOIN)
JOIN은 일반적으로 같은 값끼리 한 행으로 묶는 경우가 대부분인데, 이렇게 데이터에 조작을 가한 후 붙여 마치 LAG와 같이 쓸 수도 있다는 걸 배웠다. 잘 기억해뒀다가 필요할 때 꺼내 써야지~
* 잘 보면... 넷째줄의 DATEDIFF 구문은 위 정답 1의 마지막줄 DATE_ADD 구문으로 대체도 가능하다.
W1.RECORDDATE = DATE_ADD(W2.RECORDDATE, INTERVAL 1 DAY)
SELECT W1.ID
FROM WEATHER W1
JOIN WEATHER W2
ON DATEDIFF(W1.RECORDDATE, W2.RECORDDATE) = 1
WHERE W1.TEMPERATURE > W2.TEMPERATURE
[정답 3] 카다시안곱 활용하기 (CROSS JOIN)
자주 쓰지 않는 크로스조인이지만 이번 문제를 가장 심플하게 풀 수 있는 방법이었다.
현업에서도 실제로 (돌렸을 때 부하가 클) 크로스조인을 쓰는 경우가 있는지 궁금했다.
SELECT W1.ID
FROM WEATHER W1, WEATHER W2
WHERE W1.TEMPERATURE > W2.TEMPERATURE AND
DATEDIFF(W1.RECORDDATE, W2.RECORDDATE) = 1
1661. Average Time of Process per Machine
#JOIN
이걸 맞추는 내가 신기해짐... 언제쯤 이게 맞나? 하는 불안함 없이 이거지. 하고 담담하고 자신있게 풀 수 있게 될까?
그래도 스스로가 대견하다. 위 문제처럼 조인으로 더 쉽게 풀 수 있는 방법은 없을까?
[정답 1] WITH 구문 활용하기
WITH ACTIVITY_TIME AS (
SELECT A1.MACHINE_ID, A1.PROCESS_ID, (A2.TIMESTAMP - A1.TIMESTAMP) AS TIME_AMOUNT
FROM ACTIVITY A1
JOIN ACTIVITY A2
ON A1.MACHINE_ID = A2.MACHINE_ID
AND A1.PROCESS_ID = A2.PROCESS_ID
AND A1.ACTIVITY_TYPE <> A2.ACTIVITY_TYPE
WHERE A1.ACTIVITY_TYPE = 'START'
)
SELECT MACHINE_ID, ROUND(AVG(TIME_AMOUNT),3) AS PROCESSING_TIME
FROM ACTIVITY_TIME
GROUP BY MACHINE_ID
[정답 2] JOIN 활용하기
그냥 WITH에 쓴 걸 그대로 밖으로 빼주기만 하면 되는 거였다. 그래도 뭔가 JOIN으로 한번에 짜려면 이게... 될까.... 제발.... 얍! 하는 느낌이라면 한 번 테이블을 정리하고 뽑는 WITH가 마음이 더 편한 이상한 느낌...
SELECT A1.MACHINE_ID, ROUND(AVG(A2.TIMESTAMP - A1.TIMESTAMP),3) AS PROCESSING_TIME
FROM ACTIVITY A1
JOIN ACTIVITY A2
ON A1.MACHINE_ID = A2.MACHINE_ID
AND A1.PROCESS_ID = A2.PROCESS_ID
AND A1.ACTIVITY_TYPE <> A2.ACTIVITY_TYPE
WHERE A1.ACTIVITY_TYPE = 'START'
GROUP BY A1.MACHINE_ID
577. Employee Bonus
#JOIN
생각보다 LEFT JOIN을 쓸 일이 많구나. INNER JOIN 쓸 일이 더 많을 줄 알았는데.
SELECT E.NAME, B.BONUS
FROM EMPLOYEE E
LEFT JOIN BONUS B
ON E.EMPID = B.EMPID
WHERE B.BONUS < 1000 OR B.BONUS IS NULL
1280. Students and Examinations
#WITH #JOIN
[정답 1. WITH 구문 활용하기]
무수한 시행착오 끝에 스스로 만들어낸 정답. 힌트 안 보고 풀면 엄청 뿌듯한데 시간도 그만큼 걸려서 이게 맞나 싶다...
WITH로 표를 여러개 만들 수 있다는 걸 알게 되었다.
WITH
STUXSUB AS (
SELECT *
FROM STUDENTS, SUBJECTS
),
EXAM_COUNT AS (
SELECT STUDENT_ID, SUBJECT_NAME, COUNT(SUBJECT_NAME) AS attended_exams
FROM EXAMINATIONS
GROUP BY 1,2
)
SELECT S.STUDENT_ID, S.STUDENT_NAME, S.SUBJECT_NAME,
IFNULL (ATTENDED_EXAMS, 0) AS attended_exams
FROM STUXSUB S
LEFT JOIN EXAM_COUNT E
ON S.STUDENT_ID = E.STUDENT_ID
AND S.SUBJECT_NAME = E.SUBJECT_NAME
ORDER BY 1,3
[정답 2. 중첩 JOIN 활용하기]
이렇게 쉽게 풀 수 있는 거였냐고... 나 JOIN 여러번 쓰기 왜이렇게 무서워하냐고.....
SELECT S.STUDENT_ID, S.STUDENT_NAME, J.SUBJECT_NAME,
IFNULL(COUNT(E.SUBJECT_NAME),0) AS attended_exams
FROM STUDENTS S
CROSS JOIN SUBJECTS J
LEFT JOIN EXAMINATIONS E
USING (STUDENT_ID, SUBJECT_NAME)
GROUP BY 1,2,3
ORDER BY 1,3
570. Managers with at Least 5 Direct Reports
#WITH
⏰ 06:11
[정답 1. WITH 구문 활용하기]
WITH NUM AS (SELECT MANAGERID, COUNT(MANAGERID) AS REPORTS_NUM
FROM EMPLOYEE
GROUP BY MANAGERID
)
SELECT E.NAME
FROM EMPLOYEE E
JOIN NUM N
ON E.ID = N.MANAGERID
WHERE N.REPORTS_NUM >= 5
[정답 2. SELF JOIN과 HAVING 활용하기]
계산한 열을 따로 명시해주지 않으면 머릿속에 그려지지 않아서 불안해지는 나... 이렇게 쉽게 풀 수 있는데. SQL을 제대로 이해하고 쓰는 사람과 적당히 이해하고 쓰는 사람(=지금의 나)의 역량은 현격히 차이가 나겠다는 생각을 다시 한 번 한다.
GROUP BY에 의해 묶은 다음 조건을 걸고 싶으면 HAVING을 쓰자.
SELECT E1.NAME
FROM EMPLOYEE E1
JOIN EMPLOYEE E2
ON E1.ID = E2.MANAGERID
GROUP BY E2.MANAGERID
HAVING COUNT(E2.ID) >= 5
1934. Confirmation Rate
SUM+IF
⏰ 7:06
엣헴. 내가 이제 SUM(IF(~)) 사용법을 배워왔다 이말이야.
SELECT S.USER_ID, ROUND(SUM(IF(C.ACTION = 'CONFIRMED',1,0))/COUNT(S.USER_ID),2) AS CONFIRMATION_RATE
FROM SIGNUPS S
LEFT JOIN CONFIRMATIONS C
USING (USER_ID)
GROUP BY 1
ORDER BY 1
역시 조인... 이걸 월요일부터 넣다니 조금 실수였다 생각할 만큼 매콤했던 오늘의 진도. 그래도 반복하다보면 금방 늘 것 같아서 뿌듯하다.
나머지 문제는 내일의 진도로, 내일 추가해 수정 예정.
** 추가. 런타임의 배신... 똑같은 코드로 계속 돌려도 계속 천차만별로 다른 런타임이 나온다... 상위 10%에서 80% 수준까지...... 인터넷 속도 빠른 사람이 승자..? (신뢰도 상실)
그래도 클린코드를 증명해주는 최소한의 수단으로 붙여넣어본다.
'[공부] SQL' 카테고리의 다른 글
[SQL 코딩테스트] 리트코드 Sorting and Grouping 정답 (1) | 2024.01.24 |
---|---|
[SQL 코딩테스트] 리트코드 Basic Aggregate Functions 정답 (4) | 2024.01.23 |
[SQL 코딩테스트] 리트코드 Select 정답 (0) | 2024.01.22 |
[SQL 코딩테스트] 프로그래머스 String, Date 정답 (0) | 2024.01.18 |
[SQL 코딩테스트] 프로그래머스 IS NULL 정답 (0) | 2024.01.17 |