문제
https://school.programmers.co.kr/learn/courses/30/lessons/157339
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
✍️ 문제 요약
- 자동차 종류 : 세단 또는 SUV
- 2022-11-1 부터 2022-11-30 까지 대여 가능
- 30일간의 대여 금액이 50만원 이상 200만원 이하인 자동차
- 자동차 id, 자동차 종류, 대여 금액(컬럼:FEE) 출력
- 대여 금액 내림차순, 자동차 종류 오름차순, 자동차 id 내림차순
내가 작성한 코드
WITH carfees AS (
SELECT
c.car_id,
c.car_type,
ROUND(c.daily_fee * (1 - p.discount_rate/100) * 30) as FEE
FROM car_rental_company_car as c
JOIN car_rental_company_discount_plan as p
ON c.car_type = p.car_type
WHERE c.car_type IN ('세단', 'SUV')
AND p.duration_type = '30일 이상'
)
SELECT
cf.car_id,
cf.car_type,
cf.FEE
FROM carfees as cf
WHERE FEE BETWEEN 500000 AND 2000000
AND NOT EXISTS (
SELECT 1
FROM car_rental_company_rental_history as h
WHERE h.car_id = cf.car_id -- car_id가 같은 것 중에서
AND h.start_date < '2022-11-01'
AND h.end_date > '2022-11-30'
)
ORDER BY FEE DESC, car_type ASC, car_id DESC;
오답이다.너무 어렵다... 큰일이다! 날짜 조건이 상당히 까다로운 문제이다.
2022-11-1 부터 2022-11-30 까지 대여 가능하다?
- 우선, 11월 동안 대여 기록이 없어야 한다는 말
- 이 조건을 어떻게 걸어야 하는 거지? 범위로 표현해야 할 것 같은데, 논리가 조금 어렵다...
- h.start_date < '2022-11-30' 이면서 h.end_date > '2022-11-01' => 11월에 하루라도 대여 기록이 있는 것!!
- 이해하기가 어렵긴 한데, 이렇게 생각해보기로 하자! 하루라도 11월에 대여 기록이 있으면 안돼!!
- 그러니까 위에 조건에 해당하는 날짜를 제외시키자!! => NOT EXISTS 사용 (🚨주의 : 행을 불러오는게 아닌 참거짓 확인용)
- 다시 쉽게 풀어서 설명하면,
- 11월에 이미 대여를 시작한 차량들 => start_date
- 11월에 아직 반납이 안된 차량들 => end_date
- 합치면 : 11월에 이미 대여기록이 있는 애들
- NOT EXISTS (...) : 11월에 대여기록이 없는 애들!!
코드 설명
1. '세단' and 'SUV' 차량 | '30일 이상' | FEE 컬럼 생성('30일 이상' 대여시 할인율 적용) : WITH 문(CTE)에 담는다!!
WITH carfees AS (
SELECT
c.car_id,
c.car_type,
ROUND(c.daily_fee * (1 - p.discount_rate/100) * 30) as FEE
FROM car_rental_company_car as c
JOIN car_rental_company_discount_plan as p
ON c.car_type = p.car_type
WHERE c.car_type IN ('세단', 'SUV')
AND p.duration_type = '30일 이상'
)
2. 그 중 50만원 이상 200만원 이하인 FEE 의 조건을 주고,
WHERE FEE BETWEEN 500000 AND 2000000
3. NOT EXISTS 를 사용해서 '11월에 하루라도 대여기록 있는 차량들'이 아닌 행이 있는지(True) 확인 ⭐️⭐️⭐️⭐️⭐️
AND NOT EXISTS (
SELECT 1
FROM car_rental_company_rental_history as h
WHERE h.car_id = cf.car_id
AND h.start_date < '2022-11-30'
AND h.end_date > '2022-11-01'
)
3. 순서 정렬
ORDER BY FEE DESC, car_type ASC, car_id DESC;
최종 쿼리
- NOT EXISTS 를 활용한 풀이
WITH carfees AS (
SELECT
c.car_id,
c.car_type,
ROUND(c.daily_fee * (1 - p.discount_rate/100) * 30) as FEE
FROM car_rental_company_car as c
JOIN car_rental_company_discount_plan as p
ON c.car_type = p.car_type
WHERE c.car_type IN ('세단', 'SUV')
AND p.duration_type = '30일 이상'
)
SELECT
cf.car_id,
cf.car_type,
cf.FEE
FROM carfees as cf
WHERE FEE BETWEEN 500000 AND 2000000
AND NOT EXISTS (
SELECT 1
FROM car_rental_company_rental_history as h
WHERE h.car_id = cf.car_id
AND h.start_date < '2022-11-30'
AND h.end_date > '2022-11-01'
)
ORDER BY FEE DESC, car_type ASC, car_id DESC;
🔍 또 다른 풀이
- MAX(조건)을 활용한 풀이
- MySQL은 Boolean 값을 내부적으로 1(참)과 0(거짓)으로 처리
WITH CHECK_CAR_STATUS AS(
SELECT
CAR_ID,
MAX(START_DATE < "2022-11-30" AND END_DATE > "2022-11-01") AS STATUS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY CAR_ID)
SELECT
*
FROM(
SELECT
C1.CAR_ID,
C1.CAR_TYPE,
ROUND(DAILY_FEE * (1 - C3.DISCOUNT_RATE/100) * 30) AS FEE
FROM CAR_RENTAL_COMPANY_CAR C1
LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN C3
ON C1.CAR_TYPE = C3.CAR_TYPE
WHERE
C1.CAR_TYPE IN ('세단','SUV') AND
C1.CAR_ID IN (
SELECT
CAR_ID
FROM CHECK_CAR_STATUS
WHERE STATUS = 0) AND
C3.DURATION_TYPE = "30일 이상") SUB
WHERE FEE BETWEEN 500000 AND 2000000
ORDER BY FEE DESC, CAR_TYPE, CAR_ID DESC
틈새 문법
NOT EXISTS
- 주로 WHERE 절에서 서브쿼리와 함께 사용
SELECT column1, column2, ...
FROM table1
WHERE NOT EXISTS (
SELECT 1
FROM table2
WHERE table1.column_name = table2.column_name
);
예시
더보기
Orders 테이블과 Customers 테이블이 있을 때, 주문이 없는 고객을 찾고 싶을 때:
- Orders 테이블에 존재하지 않는 고객의 customer_id를 서브쿼리로 조회합니다.
- Customers 테이블에서 해당 customer_id를 가진 고객을 NOT EXISTS 조건에 따라 조회합니다.
SELECT c.customer_name
FROM Customers c
WHERE NOT EXISTS (
SELECT 1
FROM Orders o
WHERE c.customer_id = o.customer_id
);
이 쿼리는 Orders 테이블에 주문 기록이 없는 모든 고객의 이름을 반환한다.
✅ NOT EXISTS의 동작 방식
- 메인 쿼리 실행: Customers 테이블에서 행을 하나씩 가져옵니다.
- 서브쿼리 실행: 해당 행을 기준으로 서브쿼리가 실행됩니다.
- 결과 확인:
- 서브쿼리가 하나 이상의 행을 반환하면 NOT EXISTS는 False가 됩니다.
- 서브쿼리가 어떤 행도 반환하지 않으면 NOT EXISTS는 True가 됩니다.
- 결과 출력: NOT EXISTS가 True인 경우에만 메인 쿼리의 해당 행이 결과에 포함됩니다.
✅ EXISTS와의 차이점
- EXISTS: 서브쿼리의 결과가 존재하면 True를 반환하여 메인 쿼리의 데이터를 선택합니다.
- NOT EXISTS: 서브쿼리의 결과가 존재하지 않아야 True를 반환하여 메인 쿼리의 데이터를 선택합니다
끝.
'SQL' 카테고리의 다른 글
| [SQL/코드카타] 프로그래머스 - 자동차 대여 기록 별 대여 금액 구하기 (0) | 2025.10.02 |
|---|---|
| [SQL/복습] 세션 2회차 - JOIN | WITH 구문 | 핵심 키워드 유형 정리 (0) | 2025.09.29 |
| [SQL/QCC] 실전 연습 2차 - 코딩 테스트 2문제 (0) | 2025.09.26 |