SQL 튜터 세션 정리
오늘 푼 문제
- 문제: LeetCode 197. Rising Temperature
- 핵심 개념: 날짜 기준 self join, 하루 전 날짜 비교, MySQL 날짜 함수
- 사용 표현:
DATE_SUB(),DATE_ADD(),DATEDIFF()
문제 이해
Weather 테이블에서 전날보다 온도가 높은 날의 id를 찾는 문제다.
중요한 점은 단순히 이전 행과 비교하는 것이 아니라, recordDate 기준으로 정확히 하루 전 날짜와 비교해야 한다는 것이다.
예를 들어 다음처럼 날짜가 중간에 비어 있을 수 있다.
2021-01-01 10
2021-01-03 15
이 경우 2021-01-03의 바로 이전 행은 2021-01-01이지만, 실제 하루 전 날짜인 2021-01-02의 데이터는 없다. 따라서 이 두 행을 비교하면 안 된다.
처음 접근: 순위로 이전 일을 찾기
처음에는 날짜 순서대로 순위를 만들고, 현재 행의 순위보다 1 작은 행을 전날처럼 붙이는 방식으로 접근했다.
WITH Tmp AS (
SELECT id, recordDate, temperature, RANK() OVER(ORDER BY recordDate) AS rnk
FROM Weather
)
SELECT id
FROM (
SELECT w1.id,
w1.recordDate,
w1.temperature,
w2.temperature AS yesterday_temperature
FROM Tmp w1
LEFT JOIN Tmp w2
ON w1.rnk - 1 = w2.rnk
) AS t
WHERE temperature > yesterday_temperature;이 접근은 self join으로 비교 대상을 붙인다는 점에서는 좋다. 하지만 rnk - 1은 이전 행을 의미할 뿐, 정확히 하루 전 날짜를 의미하지 않는다.
따라서 날짜가 연속되어 있지 않은 데이터에서는 오답이 될 수 있다.
두 번째 접근: recordDate - 1
다음처럼 날짜에서 1을 빼서 어제 날짜를 만들려고 했다.
SELECT id
FROM (
SELECT w1.id,
w1.recordDate,
w1.temperature,
w2.temperature AS yesterday_temperature
FROM Weather w1
LEFT JOIN Weather w2
ON w1.recordDate - 1 = w2.recordDate
) AS t
WHERE temperature > yesterday_temperature;겉으로는 recordDate - 1이 “날짜에서 하루 빼기”처럼 보이지만, MySQL에서는 날짜 연산으로 안전하게 처리되지 않는다.
날짜에 숫자를 직접 빼면 날짜 타입 연산이 아니라 숫자처럼 변환되어 계산될 수 있다. 특히 월이나 연도가 바뀌는 경계에서 문제가 생긴다.
예를 들어 우리가 원하는 것은 다음과 같다.
2015-01-01 - 1일 = 2014-12-31
하지만 단순히 recordDate - 1처럼 쓰면 이런 날짜 경계를 안정적으로 처리한다고 보기 어렵다. 날짜는 날짜 함수로 계산하는 것이 안전하다.
최종 풀이 1: DATE_ADD() 사용
SELECT w1.id
FROM Weather w1
JOIN Weather w2
ON w1.recordDate = DATE_ADD(w2.recordDate, INTERVAL 1 DAY)
WHERE w1.temperature > w2.temperature;의미는 다음과 같다.
어제 날짜 + 1일 = 오늘 날짜
즉 w2를 어제 데이터, w1을 오늘 데이터로 보고 연결한다.
최종 풀이 2: DATE_SUB() 사용
SELECT w1.id
FROM Weather w1
JOIN Weather w2
ON DATE_SUB(w1.recordDate, INTERVAL 1 DAY) = w2.recordDate
WHERE w1.temperature > w2.temperature;의미는 다음과 같다.
오늘 날짜 - 1일 = 어제 날짜
처음에 시도했던 w1.recordDate - 1 = w2.recordDate와 의도는 같지만, DATE_SUB()를 사용하면 MySQL이 날짜 타입에 맞게 하루 전 날짜를 계산한다.
최종 풀이 3: DATEDIFF() 사용
SELECT w1.id
FROM Weather w1
JOIN Weather w2
ON DATEDIFF(w1.recordDate, w2.recordDate) = 1
WHERE w1.temperature > w2.temperature;의미는 다음과 같다.
w1 날짜 - w2 날짜 = 1일
즉 w1이 w2보다 정확히 하루 뒤인 경우만 연결한다.
날짜 함수 정리
DATE_SUB()
DATE_SUB()는 날짜에서 일정 기간을 빼는 함수다.
DATE_SUB(날짜, INTERVAL 숫자 단위)예시:
DATE_SUB('2026-06-05', INTERVAL 1 DAY)결과:
2026-06-04
이번 문제에서는 “오늘 날짜에서 하루를 빼서 어제 날짜를 찾는 방식”으로 사용할 수 있다.
DATE_SUB(w1.recordDate, INTERVAL 1 DAY) = w2.recordDateDATE_ADD()
DATE_ADD()는 날짜에 일정 기간을 더하는 함수다.
DATE_ADD(날짜, INTERVAL 숫자 단위)예시:
DATE_ADD('2026-06-04', INTERVAL 1 DAY)결과:
2026-06-05
이번 문제에서는 “어제 날짜에 하루를 더하면 오늘 날짜가 되는 방식”으로 사용할 수 있다.
w1.recordDate = DATE_ADD(w2.recordDate, INTERVAL 1 DAY)DATEDIFF()
DATEDIFF()는 두 날짜 사이의 일수 차이를 구하는 함수다.
DATEDIFF(날짜1, 날짜2)예시:
DATEDIFF('2026-06-05', '2026-06-04')결과:
1
이번 문제에서는 두 날짜의 차이가 정확히 1일인 경우만 연결하면 된다.
DATEDIFF(w1.recordDate, w2.recordDate) = 1LEFT JOIN이 꼭 필요할까?
이 문제에서는 JOIN, 즉 INNER JOIN을 써도 충분하다.
어제 데이터가 없는 날은 비교 대상이 없으므로 결과에 포함될 수 없다. LEFT JOIN을 사용하더라도 어제 온도는 NULL이 되고, 다음 조건에서 탈락한다.
WHERE temperature > yesterday_temperatureNULL과의 비교 결과는 참이 아니기 때문이다.
따라서 의도를 더 명확하게 표현하려면 JOIN을 사용하는 편이 좋다.
오늘의 메타 정리
잘한 점
- “오늘 행과 어제 행을 붙여서 비교한다”는 self join 구조를 떠올렸다.
yesterday_temperature를 따로 붙여 비교하려는 사고가 명확했다.- 처음 쿼리에서 막힌 이유를 질문하며 날짜 연산의 원리를 확인했다.
실수 패턴
RANK()로 만든 이전 행을 전날로 해석했다.recordDate - 1을 날짜에서 하루 빼는 연산으로 생각했다.
핵심 인사이트
SQL에서 날짜 문제를 풀 때는 “이전 행”과 “이전 날짜”를 구분해야 한다. 날짜는 연속되어 있지 않을 수 있으므로, 순서 기반 비교보다 날짜 차이 조건을 명시하는 것이 안전하다.