문제 설명
다음은 어느 자동차 대여 회사에서 대여 중인 자동차들의 정보를 담은
CAR_RENTAL_COMPANY_CAR
테이블과 자동차 대여 기록 정보를 담은 CAR_RENTAL_COMPANY_RENTAL_HISTORY
테이블과 자동차 종류 별 대여 기간 종류 별 할인 정책 정보를 담은 CAR_RENTAL_COMPANY_DISCOUNT_PLAN
테이블 입니다.CAR_RENTAL_COMPANY_CAR
테이블은 아래와 같은 구조로 되어있으며, CAR_ID
, CAR_TYPE
, DAILY_FEE
, OPTIONS
는 각각 자동차 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_ID
, CAR_ID
, START_DATE
, END_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_ID
, CAR_TYPE
, DURATION_TYPE
, DISCOUNT_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
테이블이 다음과 같다면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;

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 내림차순
최종 흐름 요약
- 대여일수 계산 (
RENTAL
)
- 할인정책 문자열 → 숫자 변환 (
DISCOUNT
)
- 조인해서 정책 매칭 (여러 개 가능)
- 그룹화해서 최댓값 할인율 적용
- 요금 계산 (할인 적용 or 기본 요금)
- 정렬
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 = "트럭";

할인 기간 문자열을 숫자로 변경
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 = "트럭";

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

❗문제 발생 : 할인율에 해당하지 않는 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

할인율이 적용된 값을 만들어 낸다
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

Share article