문제 요약
CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 2022년 10월 16일에 대여 중인 자동차인 경우 '대여중' 이라고 표시하고, 대여 중이지 않은 자동차인 경우 '대여 가능'을 표시하는 컬럼(컬럼명: AVAILABILITY)을 추가하여 자동차 ID와 AVAILABILITY 리스트를 출력하는 SQL문을 작성해주세요.
이때 반납 날짜가 2022년 10월 16일인 경우에도 '대여중'으로 표시해주시고 결과는 자동차 ID를 기준으로 내림차순 정렬해주세요.
👉🏻 문제보러가기
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
들어가며
문제를 풀기 전, CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블을 먼저 살펴봅시다.
각 car_id별로 대여기록이 한 행씩 저장되는 것을 알 수 있습니다.
단순히 각 행이 2022년 10월 16일에 대여중인 기록인지를 판단하여 추출하는 것이 아니라,
각 자동차가 2022년 10월 16일에 대여중이었던 기록이 있는지를 확인해야 하는 것이 관건인 문제입니다!
그렇다면 해당 날짜에 대여중이었는지는 어떻게 판단할 수 있을까요?
문제에서 반납날짜가 2022년 10월 16일인 경우에도 '대여중'으로 표시해달라고 했기 때문에, 대여날짜가 2022년 10월 16일을 포함해서 그 이전이면서, 반납날짜가 2022년 10월 16일을 포함해서 그 이후인 대여기록이 해당 날짜에 대여중인 것이죠!
쿼리로 표현하면 다음과 같습니다.
# 아래 두 가지 쿼리 모두 동일한 의미
start_date <= '2022-10-16' AND end_date >= '2022-10-16'
'2022-10-16' BETWEEN start_date AND end_date
1. 첫 번째 방식 : SELECT 절에서 서브쿼리를 사용하는 방식
각 car_id별로 대여기록이 한 행씩 저장되기 때문에, 그 중에서 2022년 10월 16일에 대여중이었는지 아니었는지를 판단하는 것이 관건입니다.
1. 2022년 10월 16일에 대여중이었던 car_id의 리스트를 추출하고,
2. GROUP BY를 통해 car_id별로 그룹핑을 해준 뒤
3. CASE문으로 해당 리스트에 포함되는 차는 '대여중', 아닌 경우 '대여 가능'으로 표시하는 방식으로 풀어보겠습니다.
STEP1 | 서브쿼리 작성 - 10월 16일에 대여중이었던 car_id 리스트 추출
SELECT DISTINCT car_id
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE start_date <= '2022-10-16' AND end_date >= '2022-10-16'
STEP2 + 3 | car_id별로 1번 리스트에 포함되는지 확인하여 AVAILABILITY를 판단
대여기록은 한 행씩 저장되기 때문에, 문제에서 원하는대로 2022년 10월 16일에 대여중이었던 car_id를 추출하기 위해서는 GROUP BY로 car_id별로 묶어주어야 합니다.
이후, CASE문에 서브쿼리를 활용하여, 각 car_id가 서브쿼리에서 추출한 car_id리스트에 포함되는지 아닌지를 체크하여 AVAILABILITY를 출력합니다.
SELECT car_id
, CASE WHEN car_id IN (SELECT DISTINCT car_id
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE start_date <= '2022-10-16' AND end_date >= '2022-10-16') THEN '대여중'
ELSE '대여 가능'
END AS availability
FROM
CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY car_id
ORDER BY car_id DESC
2. 두 번째 방식 : 서브쿼리와 SUM 함수를 활용하는 방식
첫 번째 방식으로 풀이는 가능하지만, SELECT문에 서브쿼리를 사용하여 서브쿼리의 결과를 임시 테이블에 저장해두고, 메인쿼리에서 이를 활용하기 때문에 상대적으로 리소스가 많이 들어갑니다.
보다 일반적으로 효율적인 방식을 찾기 위해 다른 방식으로도 문제를 풀어보겠습니다.
1. 각 행(대여기록)에 2022년 10월 16일에 대여중인 데이터인지를 1과 0으로 표시한다.
2. 1번을 서브쿼리로 사용하고, car_id별로 그룹핑을 해줍니다. 이 때, SUM함수를 사용해서 0보다 큰 경우 대여중인 것으로 판단한다.
STEP1 | 서브쿼리 작성 - 각 대여기록이 2022년 10월 16일에 대여중이었던 기록인지 표시한다.
SELECT car_id
, CASE WHEN '2022-10-16' BETWEEN start_date AND end_date THEN 1
ELSE 0
END AS num_availability
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
num_availability라는 컬럼을 만들어, 대여기록에 2022년 10월 16일이 포함되면 1이라고 표시해주었습니다.
STEP2+3 | SUM함수를 통해 각 자동차가 2022년 10월 16일에 대여중이었는지 판단한다.
car_id를 기준으로 그룹핑을 하면서, 해당하는 car_id의 모든 num_availability를 합쳐줍니다.
이 때, 해당 자동차가 10월 16일에 대여중이었다면 대여기록 중에 한 개 이상은 num_availability가 1일 것이기 때문에, 합쳤을 때의 값도 0보다 클 것입니다. 반대로 10월 16일에 대여 가능 상태였다면 모두 0일 것이기 때문에 합쳤을 때도 0일 것이구요!
SELECT car_id
, CASE WHEN SUM(num_availability) > 0 THEN '대여중'
ELSE '대여 가능'
END AS availability
FROM (
SELECT car_id
, CASE WHEN '2022-10-16' BETWEEN start_date AND end_date THEN 1
ELSE 0
END AS num_availability
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
) AS A
GROUP BY car_id
ORDER BY car_id DESC
어떤 방식이 더 효율적일까?
쿼리의 효율성을 고려할 때 언제나 가장 중요한 것은 데이터의 크기, 인덱스 존재 여부 등의 데이터베이스 환경에 따라 쿼리의 성능은 달라질 수 있다는 것입니다.
그러나 쿼리 구조만으로 일반적인 경우에 어떻게 쿼리를 짜는 것이 더 효율적일지 고민해보는 것은 의미가 있다고 생각합니다!
이 경우, 첫 번째 방식은 위의 언급했던 것처럼, SELECT절에 사용한 서브쿼리의 결과를 임시테이블에 저장하고, 각 car_id가 해당 테이블에 존재하는지를 판단해주어야 합니다.
반면에 두 번째 방식은 서브쿼리의 결과가 바로 메인쿼리의 FROM절에 사용되어 서브쿼리의 결과를 임시테이블에 저장하지 않아도 됩니다.
이와 같은 차이점 때문에 일반적인 경우에는 두 번째 쿼리가 더 효율적일 가능성이 높습니다!
Lessons Learned
✔️BETWEEN A AND B
A 이상 B 이하인 범위를 나타냄. 즉, A와 B 값도 범위에 포함됨
기본적인 것임에도 A와 B가 포함인지 아닌지 종종 헷갈리곤 합니다. 잊지 않도록 한 번 더 체크해봅니다🌝
'MySQL' 카테고리의 다른 글
프로그래머스 : 조건에 맞는 사용자 정보 조회하기 (0) | 2023.04.13 |
---|---|
SolveSQL : 지역별 자전거 대여 현황 문제 풀이 (다양한 방식으로 쿼리 짜보기) (0) | 2023.01.20 |
프로그래머스 : 헤비 유저가 소유한 장소 문제 풀이 (0) | 2023.01.20 |
MySQL로 데이터 가공하는 몇 가지 케이스들 (0) | 2023.01.20 |