본문 바로가기

코딩테스트/SQL - 프로그래머스

SQL 고득점 kit(JOIN) - 특정 기간동안 대여 가능한 자동차들의 대여비용 구하기

 

프로그래머스 문제

https://school.programmers.co.kr/learn/courses/30/lessons/157339

 

풀이

https://school.programmers.co.kr/questions/73884

 

 

 

 

-- 풀이 쿼리

SELECT C.CAR_ID, C.CAR_TYPE
     , ROUND((30 * C.DAILY_FEE * (1-P.DISCOUNT_RATE/100))) AS FEE
    FROM CAR_RENTAL_COMPANY_CAR C
    LEFT JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY H
        ON C.CAR_ID = H.CAR_ID
    LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN P
        ON C.CAR_TYPE = P.CAR_TYPE
    WHERE C.CAR_ID NOT IN 
    (
        SELECT CAR_ID
           FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
           WHERE ('2022-11-01 00:00:00' BETWEEN START_DATE AND END_DATE)
              OR ('2022-11-30 23:59:59' BETWEEN START_DATE AND END_DATE)
    )
        AND C.CAR_TYPE IN ('세단', 'SUV')
        AND P.DURATION_TYPE = '30일 이상'
    GROUP BY C.CAR_ID, C.CAR_TYPE
    HAVING FEE BETWEEN 500000 AND 2000000
    ORDER BY FEE DESC, C.CAR_TYPE, C.CAR_ID DESC;

 

좀 길어 보이지만 차근차근 해석하면 어려울 것은 없다.

이 문제가 좀 까다로웠던 건 이런저런 조건이 많이 붙었기 때문.

 

-- 할인 가능 대여기간 : 대여 7~29일, 대여 30~89일, 대여 90일 ~
-- 차종 : 세단, SUV
-- 기간 : 2022-11-01 ~ 2022-11-30, 총 30일간 대여.
-- 대여금액 : 50만원 ~ 199만 9999원
-- 출력 : 대여 가능한 자동차ID, 자동차 종류, 대여금액(컬럼명 : FEE, 정수 형태.)
-- 정렬 : 대여금액 내림차순 차종 오름차순 차ID 내림차순

 

즉 22년 11월 1일 ~ 11월 30일, 총 30일간 대여금액 50만원~199만 9999원으로 세단 or SUV를 대여할건데, 대여 가능한 자동차 ID, 종류, 예상 대여금액을 정렬 기준에 맞춰서 출력하면 되는 것.

 

 

본인이 작성한 쿼리문에서 핵심이 되는 WHERE절 부분만 해석해보자.

 

WHERE C.CAR_ID NOT IN 
(
    SELECT CAR_ID
       FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
       WHERE ('2022-11-01 00:00:00' BETWEEN START_DATE AND END_DATE)
          OR ('2022-11-30 23:59:59' BETWEEN START_DATE AND END_DATE)
)

 

이 쿼리는 22년 11월 1일 ~ 11월 30일 사이에 대여가 가능한 자동차 ID를 출력하는 쿼리다.

 

우선 WHERE절 안의 쿼리를 보자.

해당 쿼리는 문제에서 요구하는 기간에 대여가 불가능한 자동차 ID를 출력한다.

START_DATE와 END_DATE 사이에 11월 1일이나 11월 30일 둘 중 하나라도 있다면, 대여가 불가능 할 것이니까.

 

그리고 NOT IN 구문으로, 바깥 메인 쿼리에서는 대여가 불가능한 자동차 ID를 제외한 ID를 출력하게끔 한다.

  => 대여가 가능한 자동차 ID 출력 

 

그 외에는 문제에서 주어진 조건에 맞춰서 간단하게 쿼리를 작성하면 되기 때문에 크게 어려울 부분은 없다.

 

 

 

24.04.11 수정)

 

내가 짠 쿼리는, 11/4가 시작일이고 11/6이 마감일인 경우의 ID는 고려하지 못하는 것 아닌가? 하는 지적이 있었다.

그리고, 생각해보니 내가 틀린게 맞았다(지적해 주셔서 감사합니닷)

 

그래서 해당 부분도 고려할 수 있게 고쳐보았는데, 고치는 김에 전체 쿼리를 조금이나마 더 짧게끔 고쳐 보았다.

-- 수정한 쿼리
SELECT C.CAR_ID, C.CAR_TYPE
     , ROUND((30 * C.DAILY_FEE * (1 - P.DISCOUNT_RATE / 100))) AS FEE
    FROM CAR_RENTAL_COMPANY_CAR C
    LEFT JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY H
        ON C.CAR_ID = H.CAR_ID
        AND (H.START_DATE <= '2022-11-30' AND H.END_DATE >= '2022-11-01')
    LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN P
        ON C.CAR_TYPE = P.CAR_TYPE
        AND P.DURATION_TYPE = '30일 이상'
    WHERE C.CAR_TYPE IN ('세단', 'SUV')
        AND H.CAR_ID IS NULL
    GROUP BY C.CAR_ID, C.CAR_TYPE
    HAVING FEE BETWEEN 500000 AND 2000000
    ORDER BY FEE DESC, C.CAR_TYPE ASC, C.CAR_ID DESC;

 

짧아졌나...? 개미 몸길이만큼 짧아진 것 같긴 하다.

 

 

수정된 부분을 설명해보자면,

 

- 서브쿼리를 쓰지 않기 위해 JOIN할 때 조건을 좀 더 추가했다.

- 첫번째 LEFT JOIN에서 START_DATE, END_DATE에 대한 조건을 걸어줌으로써 해당 조건에 해당하지 않는, 즉 START_DATE가 11/30보다 크고 END_DATE가 11/01보다 작은 경우(=11월에 대여 기록이 없는 경우)의 H.CAR_ID는 NULL값으로 나온다.

- WHERE절에서 H.CAR_ID가 NULL인 데이터로 필터링함으로써 11월에 대여기록이 없는 경우의 CAR ID를 출력할 수 있게 된다.