[Programmers] 대여 횟수가 많은 자동차들의 월별 대여 횟수 구하기

이정진·2023년 1월 25일
12

SQL

목록 보기
19/23
post-thumbnail

대여 횟수가 많은 자동차들의 월별 대여 횟수 구하기

난이도 구분 : Level 3

문제

CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 대여 시작일을 기준으로 2022년 8월부터 2022년 10월까지 총 대여 횟수가 5회 이상인 자동차들에 대해서 해당 기간 동안의 월별 자동차 ID 별 총 대여 횟수(컬럼명: RECORDS) 리스트를 출력하는 SQL문을 작성해주세요. 결과는 월을 기준으로 오름차순 정렬하고, 월이 같다면 자동차 ID를 기준으로 내림차순 정렬해주세요. 특정 월의 총 대여 횟수가 0인 경우에는 결과에서 제외해주세요.

문제 풀이

  1. DATE_FORMAT을 주어진 조건에 맞추어 찾기 + CAR_ID와 MONTH를 기준으로 그룹화하기 + 대여 시작일을 기준으로 2022년 8월부터 2022년 10월까지 조건 걸기

SQL

SELECT DATE_FORMAT(START_DATE, '%c') MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
GROUP BY MONTH, CAR_ID;

결과

2.총 대여 횟수가 5회 이상인 자동차들에 대해 해당 기간 동안의 월별 자동차 ID 별 총 대여 횟수(컬럼명: RECORDS) 리스트를 출력 = 총 대여 횟수가 5회 이상인 자동차들의 CAR_ID를 찾는 조건문 추가
GROUP BY + HAVING의 특징 (출처)

  • GROUP BY 절은 데이터들을 원하는 그룹으로 나눌 수 있다.
  • 집계함수와 함께 사용되는 상수는 GROUP BY 절에 추가하지 않아도 된다.
  • HAVING 절은 집계함수를 가지고 조건비교를 할 때 사용한다.
  • HAVING절은 GROUP BY절과 함께 사용이 된다.

SQL

SELECT DATE_FORMAT(START_DATE, '%c') MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
GROUP BY CAR_ID
HAVING COUNT(HISTORY_ID) >= 5;

결과

  1. 월을 기준으로 오름차순 정렬 + 월이 같다면 자동차 ID를 기준으로 내림차순 정렬 +
    특정 월의 총 대여 횟수가 0인 경우 제외

SQL

SELECT MONTH(START_DATE) AS MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE CAR_ID IN (
        SELECT CAR_ID
        FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
        WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
        GROUP BY CAR_ID
        HAVING COUNT(CAR_ID) >= 5
    ) AND (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
GROUP BY MONTH, CAR_ID
HAVING RECORDS > 0
ORDER BY MONTH ASC, CAR_ID DESC;

결과

정답 SQL

SELECT MONTH(START_DATE) AS MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE CAR_ID IN (
        SELECT CAR_ID
        FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
        WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
        GROUP BY CAR_ID
        HAVING COUNT(CAR_ID) >= 5
    ) AND (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
GROUP BY MONTH, CAR_ID
HAVING RECORDS > 0
ORDER BY MONTH ASC, CAR_ID DESC;

풀이 과정에서 만난 어려움

MONTH() VS DATE_FORMAT()

문제 풀이 중 3번 과정에서 ORDER BY를 순차적으로 사용하여 진행하였으나, 아래의 결과값과 같이 나와서 이유를 찾고자 하였으나 찾지 못했다.

SQL

SELECT DATE_FORMAT(START_DATE, '%c') MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE CAR_ID IN (
        SELECT CAR_ID
        FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
        WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
        GROUP BY CAR_ID
        HAVING COUNT(CAR_ID) >= 5
    )
GROUP BY MONTH, CAR_ID
ORDER BY MONTH ASC, CAR_ID DESC;

결과

이를 해결하기 위해 알아보니, MONTH()를 활용한 경우가 일반적이기에, MONTH()를 이용한 방식으로 풀이했다.

SQL

SELECT MONTH(START_DATE) AS MONTH, CAR_ID, COUNT(HISTORY_ID) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE CAR_ID IN (
        SELECT CAR_ID
        FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
        WHERE (DATE_FORMAT(START_DATE, '%Y-%m') BETWEEN '2022-08' AND '2022-10')
        GROUP BY CAR_ID
        HAVING COUNT(CAR_ID) >= 5
    )
GROUP BY MONTH, CAR_ID
ORDER BY MONTH ASC, CAR_ID DESC;

결과

2022년 8월 ~ 2022년 10월에 해당하는 조건 두 번 사용

문제 풀이 3번 과정을 보면, CAR_ID IN ()의 서브쿼리에서 WHERE 조건으로 2022년 8월 ~ 2022년 10월에 해당하는 경우만 카운팅하도록 짰기에, 이를 그대로 제출하면 된다고 판단하여 제출하였으나 WA였다. 그리하여 메인 쿼리에서 다시 한 번 동일 조건문을 작성하여 제출하였더니 AC를 받았다. 그렇기에, "왜 조건을 두 번 사용하여야 할까?"에 대하여 찾아보고자 한다.

문제 출력 데이터들을 비교하면서 찾아봤다.
첫 번째로, 서브 쿼리에만 기간 관련 조건을 넣었을 때의 결과이다.

MONTH CAR_ID RECORDS
8 28 3
8 27 5
8 25 5
8 23 3
8 19 1
8 15 2
8 13 5
8 11 1
8 10 3
8 8 6
8 7 4
8 5 4
8 2 5
9 28 2
9 27 1
9 25 6
9 23 4
9 21 1
9 20 4
9 19 3
9 18 4
9 15 2
9 13 4
9 12 4
9 11 6
9 10 5
9 8 5
9 7 1
9 5 1
9 2 1
10 23 1
10 21 5
10 20 2
10 19 1
10 18 3
10 15 2
10 13 5
10 12 2
10 10 3
10 8 5
10 7 1
10 2 1

두 번째로, 전체 쿼리에 기간 관련 조건을 넣었을 때의 결과이다.

MONTH CAR_ID RECORDS
8 28 3
8 27 5
8 25 5
8 23 3
8 19 1
8 15 2
8 13 5
8 11 1
8 10 3
8 8 6
8 7 4
8 5 4
8 2 5
9 28 2
9 27 1
9 25 6
9 23 4
9 21 1
9 20 4
9 19 3
9 18 4
9 15 2
9 13 4
9 12 4
9 11 6
9 10 5
9 8 5
9 7 1
9 5 1
9 2 1
10 23 1
10 21 5
10 20 2
10 19 1
10 18 3
10 15 2
10 13 5
10 12 2
10 10 3
10 8 5
10 7 1
10 2 1

조건을 다르게 하여도 동일한 결과값이 출력되기에,

SELECT CAR_ID, START_DATE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY

위 SQL을 활용하여 현재 저장되어 있는 데이터들은 무엇이 있는지 확인해보았다.

CAR_ID START_DATE
15 2022-08-01 00:00:00
26 2022-08-01 00:00:00
27 2022-08-01 00:00:00
29 2022-08-02 00:00:00
13 2022-08-03 00:00:00
27 2022-08-03 00:00:00
28 2022-08-03 00:00:00
2 2022-08-05 00:00:00
5 2022-08-05 00:00:00
23 2022-08-05 00:00:00
25 2022-08-05 00:00:00
27 2022-08-05 00:00:00
7 2022-08-06 00:00:00
8 2022-08-06 00:00:00
8 2022-08-07 00:00:00
19 2022-08-07 00:00:00
28 2022-08-07 00:00:00
29 2022-08-07 00:00:00
23 2022-08-08 00:00:00
25 2022-08-08 00:00:00
2 2022-08-09 00:00:00
5 2022-08-09 00:00:00
27 2022-08-09 00:00:00
2 2022-08-10 00:00:00
5 2022-08-10 00:00:00
9 2022-08-10 00:00:00
15 2022-08-10 00:00:00
25 2022-08-11 00:00:00
5 2022-08-12 00:00:00
8 2022-08-12 00:00:00
13 2022-08-12 00:00:00
16 2022-08-13 00:00:00
29 2022-08-13 00:00:00
7 2022-08-14 00:00:00
25 2022-08-14 00:00:00
8 2022-08-16 00:00:00
6 2022-08-17 00:00:00
13 2022-08-17 00:00:00
16 2022-08-17 00:00:00
25 2022-08-17 00:00:00
9 2022-08-18 00:00:00
7 2022-08-19 00:00:00
10 2022-08-20 00:00:00
2 2022-08-21 00:00:00
29 2022-08-21 00:00:00
9 2022-08-22 00:00:00
7 2022-08-23 00:00:00
13 2022-08-23 00:00:00
2 2022-08-24 00:00:00
27 2022-08-24 00:00:00
10 2022-08-25 00:00:00
23 2022-08-25 00:00:00
28 2022-08-25 00:00:00
8 2022-08-26 00:00:00
10 2022-08-27 00:00:00
13 2022-08-27 00:00:00
8 2022-08-29 00:00:00
11 2022-08-29 00:00:00
8 2022-09-01 00:00:00
10 2022-09-01 00:00:00
25 2022-09-01 00:00:00
12 2022-09-02 00:00:00
10 2022-09-03 00:00:00
11 2022-09-04 00:00:00
23 2022-09-04 00:00:00
2 2022-09-05 00:00:00
20 2022-09-05 00:00:00
18 2022-09-06 00:00:00
22 2022-09-06 00:00:00
25 2022-09-06 00:00:00
22 2022-09-07 00:00:00
24 2022-09-08 00:00:00
18 2022-09-09 00:00:00
20 2022-09-09 00:00:00
13 2022-09-10 00:00:00
4 2022-09-11 00:00:00
28 2022-09-11 00:00:00
11 2022-09-12 00:00:00
25 2022-09-12 00:00:00
8 2022-09-13 00:00:00
18 2022-09-13 00:00:00
28 2022-09-13 00:00:00
11 2022-09-14 00:00:00
13 2022-09-14 00:00:00
20 2022-09-14 00:00:00
24 2022-09-14 00:00:00
23 2022-09-15 00:00:00
8 2022-09-16 00:00:00
12 2022-09-16 00:00:00
18 2022-09-16 00:00:00
20 2022-09-16 00:00:00
8 2022-09-17 00:00:00
11 2022-09-17 00:00:00
19 2022-09-18 00:00:00
24 2022-09-18 00:00:00
11 2022-09-19 00:00:00
15 2022-09-20 00:00:00
25 2022-09-20 00:00:00
8 2022-09-21 00:00:00
12 2022-09-21 00:00:00
19 2022-09-21 00:00:00
10 2022-09-22 00:00:00
23 2022-09-22 00:00:00
5 2022-09-23 00:00:00
7 2022-09-23 00:00:00
13 2022-09-23 00:00:00
6 2022-09-24 00:00:00
12 2022-09-24 00:00:00
10 2022-09-25 00:00:00
11 2022-09-25 00:00:00
21 2022-09-25 00:00:00
25 2022-09-25 00:00:00
27 2022-09-25 00:00:00
15 2022-09-26 00:00:00
19 2022-09-26 00:00:00
23 2022-09-26 00:00:00
10 2022-09-28 00:00:00
13 2022-09-28 00:00:00
25 2022-09-29 00:00:00
15 2022-10-01 00:00:00
21 2022-10-01 00:00:00
8 2022-10-02 00:00:00
17 2022-10-02 00:00:00
9 2022-10-03 00:00:00
12 2022-10-03 00:00:00
19 2022-10-05 00:00:00
13 2022-10-06 00:00:00
8 2022-10-07 00:00:00
12 2022-10-07 00:00:00
21 2022-10-07 00:00:00
2 2022-10-10 00:00:00
7 2022-10-14 00:00:00
13 2022-10-14 00:00:00
23 2022-10-14 00:00:00
21 2022-10-17 00:00:00
10 2022-10-18 00:00:00
18 2022-10-19 00:00:00
21 2022-10-19 00:00:00
10 2022-10-20 00:00:00
3 2022-10-21 00:00:00
18 2022-10-21 00:00:00
20 2022-10-21 00:00:00
15 2022-10-22 00:00:00
4 2022-10-23 00:00:00
8 2022-10-23 00:00:00
13 2022-10-23 00:00:00
10 2022-10-24 00:00:00
6 2022-10-25 00:00:00
8 2022-10-25 00:00:00
18 2022-10-25 00:00:00
3 2022-10-27 00:00:00
8 2022-10-27 00:00:00
13 2022-10-27 00:00:00
30 2022-10-27 00:00:00
13 2022-10-28 00:00:00
6 2022-10-30 00:00:00
21 2022-10-30 00:00:00
1 2022-10-31 00:00:00
4 2022-10-31 00:00:00
20 2022-10-31 00:00:00

위와 같이, 처음부터 저장되어 있던 데이터들이 8월과 10월 사이에만 해당되기에 문제의 데이터를 통해 비교하는 과정은 불가능할 것이라고 판단했다.

가상의 데이터를 만들어서 비교를 해보고자 한다.

대여 시작일이 8월 ~ 10월 사이가 아닌 데이터들이 존재한다면,
EX) 동일한 CAR_ID로 대여 시작일이 7월인 케이스가 한 건 이상 존재 + 8월 ~ 10월 사이 대여 케이스가 5건 이상
위와 같은 상황이라면, 메인 쿼리에 8월부터 10월 사이에 대한 WHERE 조건문이 없다면 COUNT(HISTORY_ID)의 집계값은 7월 케이스의 COUNT와 8월 ~ 10월 사이 케이스의 COUNT를 합한 값이 출력될 것이다.
즉, 출력되는 COUNT(HISTORY_ID)도 8월 ~ 10월 사이에 기간으로 제한을 둬야 문제의 조건에 맞게 8월 ~ 10월 사이에 대한 집계만 진행된다는 것을 알 수 있다.

느낀 점

체감이 거진 LEVEL 4 이상이였다. GROUP BY + 서브 쿼리 방식을 구현해내는 과정에 대한 학습이 필요할 것 같고, WHERE조건문을 두 번 쓰는 것처럼 문제의 조건에 맞추어 발생할 수 있는 케이스들에 대한 사고를 해볼 수 있도록 노력해야할 것 같다.

1개의 댓글

comment-user-thumbnail
2023년 2월 5일

2022년 8월 ~ 2022년 10월에 해당하는 조건 두 번 사용
에 대해서 너무 궁금해서 검색하다가 들어온 벨로그.
설명감사합니다....

답글 달기