[프로그래머스] (MySQL) 73. 자동차 대여 기록 별 대여 금액 구하기

최재원's avatar
Sep 23, 2025
[프로그래머스] (MySQL) 73. 자동차 대여 기록 별 대여 금액 구하기
💡

문제 설명

다음은 어느 자동차 대여 회사에서 대여 중인 자동차들의 정보를 담은 CAR_RENTAL_COMPANY_CAR 테이블과 자동차 대여 기록 정보를 담은 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블과 자동차 종류 별 대여 기간 종류 별 할인 정책 정보를 담은 CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블 입니다.
CAR_RENTAL_COMPANY_CAR 테이블은 아래와 같은 구조로 되어있으며, CAR_IDCAR_TYPEDAILY_FEEOPTIONS 는 각각 자동차 ID, 자동차 종류, 일일 대여 요금(원), 자동차 옵션 리스트를 나타냅니다.
Column name
Type
Nullable
CAR_ID
INTEGER
FALSE
CAR_TYPE
VARCHAR(255)
FALSE
DAILY_FEE
INTEGER
FALSE
OPTIONS
VARCHAR(255)
FALSE
자동차 종류는 '세단', 'SUV', '승합차', '트럭', '리무진' 이 있습니다. 자동차 옵션 리스트는 콤마(',')로 구분된 키워드 리스트(예: ''열선시트,스마트키,주차감지센서'')로 되어있으며, 키워드 종류는 '주차감지센서', '스마트키', '네비게이션', '통풍시트', '열선시트', '후방카메라', '가죽시트' 가 있습니다.
CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블은 아래와 같은 구조로 되어있으며, HISTORY_IDCAR_IDSTART_DATEEND_DATE 는 각각 자동차 대여 기록 ID, 자동차 ID, 대여 시작일, 대여 종료일을 나타냅니다.
Column name
Type
Nullable
HISTORY_ID
INTEGER
FALSE
CAR_ID
INTEGER
FALSE
START_DATE
DATE
FALSE
END_DATE
DATE
FALSE
CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블은 아래와 같은 구조로 되어있으며, PLAN_IDCAR_TYPEDURATION_TYPEDISCOUNT_RATE 는 각각 요금 할인 정책 ID, 자동차 종류, 대여 기간 종류, 할인율(%)을 나타냅니다.
Column name
Type
Nullable
PLAN_ID
INTEGER
FALSE
CAR_TYPE
VARCHAR(255)
FALSE
DURATION_TYPE
VARCHAR(255)
FALSE
DISCOUNT_RATE
INTEGER
FALSE
할인율이 적용되는 대여 기간 종류로는 '7일 이상' (대여 기간이 7일 이상 30일 미만인 경우), '30일 이상' (대여 기간이 30일 이상 90일 미만인 경우), '90일 이상' (대여 기간이 90일 이상인 경우) 이 있습니다. 대여 기간이 7일 미만인 경우 할인정책이 없습니다.
💡

문제

CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블과 CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블에서 자동차 종류가 '트럭'인 자동차의 대여 기록에 대해서 대여 기록 별로 대여 금액(컬럼명: FEE)을 구하여 대여 기록 ID와 대여 금액 리스트를 출력하는 SQL문을 작성해주세요. 결과는 대여 금액을 기준으로 내림차순 정렬하고, 대여 금액이 같은 경우 대여 기록 ID를 기준으로 내림차순 정렬해주세요.

예시

예를 들어 CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블과 CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블이 다음과 같다면
CAR_ID
CAR_TYPE
DAILY_FEE
OPTIONS
1
트럭
26000
가죽시트,열선시트,후방카메라
2
SUV
14000
스마트키,네비게이션,열선시트
3
트럭
32000
주차감지센서,후방카메라,가죽시트
HISTORY_ID
CAR_ID
START_DATE
END_DATE
1
1
2022-07-27
2022-08-02
2
1
2022-08-03
2022-08-04
3
2
2022-08-05
2022-08-05
4
2
2022-08-09
2022-08-12
5
3
2022-09-16
2022-10-15
PLAN_ID
CAR_TYPE
DURATION_TYPE
DISCOUNT_RATE
1
트럭
7일 이상
5%
2
트럭
30일 이상
7%
3
트럭
90일 이상
10%
4
세단
7일 이상
5%
5
세단
30일 이상
10%
6
세단
90일 이상
15%
자동차 종류가 '트럭' 인 자동차의 대여 기록에 대해서 대여 기간을 구하면,
  • 대여 기록 ID가 1인 경우, 7일
  • 대여 기록 ID가 2인 경우, 2일
  • 대여 기록 ID가 5인 경우, 30일입니다.
대여 기간 별로 일일 대여 요금에 알맞은 할인율을 곱하여 금액을 구하면 다음과 같습니다.
  • 대여 기록 ID가 1인 경우, 일일 대여 금액 26,000원에서 5% 할인율을 적용하고 7일을 곱하면 총 대여 금액은 172,900원
  • 대여 기록 ID가 2인 경우, 일일 대여 금액 26,000원에 2일을 곱하면 총 대여 금액은 52,000원
  • 대여 기록 ID가 5인 경우, 일일 대여 금액 32,000원에서 7% 할인율을 적용하고 30일을 곱하면 총 대여 금액은 892,800원이 되므로, 대여 금액을 기준으로 내림차순 정렬 및 대여 기록 ID를 기준으로 내림차순 정렬하면 다음과 같아야 합니다.
HISTORY_ID
FEE
5
892800
1
172900
2
52000

주의사항

FEE의 경우 예시처럼 정수부분만 출력되어야 합니다.
 

코드

SELECT C2.HISTORY_ID, IF(MAX(C3.DISCOUNT_RATE) > 0, TRUNCATE((DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE * (1 - MAX(C3.DISCOUNT_RATE) / 100.0), 0), (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE) AS FEE FROM CAR_RENTAL_COMPANY_CAR AS C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY AS C2 ON C1.CAR_ID = C2.CAR_ID LEFT OUTER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN AS C3 ON C1.CAR_TYPE = C3.CAR_TYPE AND DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 > CAST(REPLACE(C3.DURATION_TYPE, "일 이상", "") AS UNSIGNED) WHERE C1.CAR_TYPE = "트럭" GROUP BY C2.HISTORY_ID ORDER BY FEE DESC, C2.HISTORY_ID DESC;
notion image

GPT 코드

SELECT H.HISTORY_ID, FLOOR(C.DAILY_FEE * (DATEDIFF(H.END_DATE, H.START_DATE) + 1) * (1 - IFNULL(DP.DISCOUNT_RATE, 0) / 100) ) AS FEE FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H JOIN CAR_RENTAL_COMPANY_CAR C ON H.CAR_ID = C.CAR_ID LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN DP ON DP.CAR_TYPE = '트럭' AND ( (DP.DURATION_TYPE = '7일 이상' AND DATEDIFF(H.END_DATE, H.START_DATE) + 1 >= 7 AND DATEDIFF(H.END_DATE, H.START_DATE) + 1 < 30) OR (DP.DURATION_TYPE = '30일 이상' AND DATEDIFF(H.END_DATE, H.START_DATE) + 1 >= 30 AND DATEDIFF(H.END_DATE, H.START_DATE) + 1 < 90) OR (DP.DURATION_TYPE = '90일 이상' AND DATEDIFF(H.END_DATE, H.START_DATE) + 1 >= 90) ) WHERE C.CAR_TYPE = '트럭' ORDER BY FEE DESC, H.HISTORY_ID DESC;

GPT가 수정한 나의 쿼리 (가독성 위주)

SELECT R.HISTORY_ID, IF(MAX(D.DISCOUNT_RATE) > 0, FLOOR(R.RENTAL_DAYS * C.DAILY_FEE * (1 - MAX(D.DISCOUNT_RATE) / 100.0)), R.RENTAL_DAYS * C.DAILY_FEE ) AS FEE FROM ( SELECT H.HISTORY_ID, H.CAR_ID, DATEDIFF(H.END_DATE, H.START_DATE) + 1 AS RENTAL_DAYS FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H ) R JOIN CAR_RENTAL_COMPANY_CAR C ON R.CAR_ID = C.CAR_ID LEFT JOIN ( SELECT CAR_TYPE, CAST(REPLACE(DURATION_TYPE, '일 이상', '') AS UNSIGNED) AS MIN_DAYS, DISCOUNT_RATE FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN ) D ON C.CAR_TYPE = D.CAR_TYPE AND R.RENTAL_DAYS > D.MIN_DAYS WHERE C.CAR_TYPE = '트럭' GROUP BY R.HISTORY_ID ORDER BY FEE DESC, R.HISTORY_ID DESC;
설명

1. 내부 서브쿼리 R (대여 이력과 대여일수 계산)

( SELECT H.HISTORY_ID, H.CAR_ID, DATEDIFF(H.END_DATE, H.START_DATE) + 1 AS RENTAL_DAYS FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H ) R
  • 대여 기록 테이블(RENTAL_HISTORY)에서 대여일수(RENTAL_DAYS) 를 미리 계산
    • DATEDIFF(END_DATE, START_DATE) + 1 → 시작일·종료일 포함이라 +1
  • 결과 예시:
    • HISTORY_ID | CAR_ID | RENTAL_DAYS -----------+--------+------------ 1 | 1 | 7 2 | 1 | 2 5 | 3 | 30

2. 내부 서브쿼리 D (할인 정책 숫자 변환)

( SELECT CAR_TYPE, CAST(REPLACE(DURATION_TYPE, '일 이상', '') AS UNSIGNED) AS MIN_DAYS, DISCOUNT_RATE FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN ) D
  • DURATION_TYPE'7일 이상', '30일 이상' 처럼 문자열임.
  • REPLACE(..., '일 이상', '')'7', '30' 으로 변환
  • CAST(... AS UNSIGNED) → 숫자로 변환해 비교 가능하게 만듦
  • 결과 예시:
    • CAR_TYPE | MIN_DAYS | DISCOUNT_RATE ---------+----------+-------------- 트럭 | 7 | 5 트럭 | 30 | 7 트럭 | 90 | 10

3. 메인 쿼리 조인

FROM R JOIN CAR_RENTAL_COMPANY_CAR C ON R.CAR_ID = C.CAR_ID LEFT JOIN D ON C.CAR_TYPE = D.CAR_TYPE AND R.RENTAL_DAYS > D.MIN_DAYS WHERE C.CAR_TYPE = '트럭'
  • R(대여일수) + CAR(차량 요금) → 기본 정보 연결
  • D(할인 정책) 과 LEFT JOIN → 해당 차량 종류 + 대여일수 조건을 만족하는 정책만 매칭
  • 예를 들어 30일 대여MIN_DAYS = 7, 30 조건 모두 만족 → 두 개가 매칭됨

4. 그룹화 및 할인율 선택

GROUP BY R.HISTORY_ID
  • 한 이력(HISTORY_ID) 당 여러 할인 조건이 붙을 수 있으므로 그룹화 필요
  • 예: 30일 → 7일 이상, 30일 이상 정책 모두 붙음 → GROUP BY 해서 합쳐야 함

5. 할인율 적용 (IF + MAX)

IF(MAX(D.DISCOUNT_RATE) > 0, FLOOR(R.RENTAL_DAYS * C.DAILY_FEE * (1 - MAX(D.DISCOUNT_RATE) / 100.0)), R.RENTAL_DAYS * C.DAILY_FEE ) AS FEE
  • MAX(D.DISCOUNT_RATE) → 여러 조건 중 가장 큰 할인율 선택 (예: 30일 → 7% 적용)
  • 할인율이 없으면(NULL) 기본 요금 계산
  • FLOOR → 소수점 버리고 정수만 출력

6. 최종 정렬

ORDER BY FEE DESC, R.HISTORY_ID DESC
  • 대여금액(FEE) 내림차순
  • 금액 같으면 HISTORY_ID 내림차순

최종 흐름 요약

  1. 대여일수 계산 (RENTAL)
  1. 할인정책 문자열 → 숫자 변환 (DISCOUNT)
  1. 조인해서 정책 매칭 (여러 개 가능)
  1. 그룹화해서 최댓값 할인율 적용
  1. 요금 계산 (할인 적용 or 기본 요금)
  1. 정렬

IFNULL 로 변경

SELECT R.HISTORY_ID, FLOOR(R.RENTAL_DAYS * C.DAILY_FEE * (1 - IFNULL(MAX(D.DISCOUNT_RATE), 0) / 100.0)) AS FEE FROM ( SELECT H.HISTORY_ID, H.CAR_ID, DATEDIFF(H.END_DATE, H.START_DATE) + 1 AS RENTAL_DAYS FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H ) R JOIN CAR_RENTAL_COMPANY_CAR C ON R.CAR_ID = C.CAR_ID LEFT JOIN ( SELECT CAR_TYPE, CAST(REPLACE(DURATION_TYPE, '일 이상', '') AS UNSIGNED) AS MIN_DAYS, DISCOUNT_RATE FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN ) D ON C.CAR_TYPE = D.CAR_TYPE AND R.RENTAL_DAYS > D.MIN_DAYS WHERE C.CAR_TYPE = '트럭' GROUP BY R.HISTORY_ID ORDER BY FEE DESC, R.HISTORY_ID DESC;
 

과정

대여기간 구하고 JOIN 으로 할인 기간을 붙였을 때
SELECT C2.HISTORY_ID, C1.CAR_ID, C1.CAR_TYPE, DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 AS "대여기간", (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE AS FEE, C3.DURATION_TYPE FROM CAR_RENTAL_COMPANY_CAR C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY C2 ON C1.CAR_ID = C2.CAR_ID INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN C3 ON C1.CAR_TYPE = C3.CAR_TYPE WHERE C3.CAR_TYPE = "트럭";
notion image
할인 기간 문자열을 숫자로 변경
SELECT C2.HISTORY_ID, C1.CAR_ID, C1.CAR_TYPE, DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 AS "대여기간", (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE AS FEE, CAST(REPLACE(C3.DURATION_TYPE, "일 이상", "") AS UNSIGNED) FROM CAR_RENTAL_COMPANY_CAR C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY C2 ON C1.CAR_ID = C2.CAR_ID INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN C3 ON C1.CAR_TYPE = C3.CAR_TYPE WHERE C3.CAR_TYPE = "트럭";
notion image
ON 절에 대여기간이 할인율 보다 큰것만 찾는다
SELECT C2.HISTORY_ID, C1.CAR_ID, C1.CAR_TYPE, DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 AS "대여기간", (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE AS FEE, MAX(C3.DISCOUNT_RATE) AS DISCOUNT_RATE FROM CAR_RENTAL_COMPANY_CAR C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY C2 ON C1.CAR_ID = C2.CAR_ID INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN C3 ON C1.CAR_TYPE = C3.CAR_TYPE AND DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 >= CAST(REPLACE(C3.DURATION_TYPE, "일 이상", "") AS UNSIGNED) WHERE C3.CAR_TYPE = "트럭" GROUP BY C2.HISTORY_ID
notion image
❗문제 발생 : 할인율에 해당하지 않는 1일 대여 같은 값이 제거가 되었다
다시 아우터 조인 방식으로 변경
SELECT C2.HISTORY_ID, C1.CAR_ID, C1.CAR_TYPE, DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 AS "대여기간", (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE AS FEE, MAX(C3.DISCOUNT_RATE) FROM CAR_RENTAL_COMPANY_CAR AS C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY AS C2 ON C1.CAR_ID = C2.CAR_ID LEFT OUTER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN AS C3 ON C1.CAR_TYPE = C3.CAR_TYPE AND DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 > CAST(REPLACE(C3.DURATION_TYPE, "일 이상", "") AS UNSIGNED) WHERE C1.CAR_TYPE = "트럭" GROUP BY C2.HISTORY_ID
notion image
할인율이 적용된 값을 만들어 낸다
SELECT C2.HISTORY_ID, C1.CAR_ID, C1.CAR_TYPE, DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 AS "대여기간", (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE AS FEE, MAX(C3.DISCOUNT_RATE), IF(MAX(C3.DISCOUNT_RATE) > 0, (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE * (1 - MAX(C3.DISCOUNT_RATE) / 100.0), (DATEDIFF(C2.END_DATE, C2.START_DATE) + 1) * C1.DAILY_FEE) FROM CAR_RENTAL_COMPANY_CAR AS C1 INNER JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY AS C2 ON C1.CAR_ID = C2.CAR_ID LEFT OUTER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN AS C3 ON C1.CAR_TYPE = C3.CAR_TYPE AND DATEDIFF(C2.END_DATE, C2.START_DATE) + 1 > CAST(REPLACE(C3.DURATION_TYPE, "일 이상", "") AS UNSIGNED) WHERE C1.CAR_TYPE = "트럭" GROUP BY C2.HISTORY_ID
notion image
 
Share article

jjack1