2025. 3. 22. 10:29ㆍSQL 문제풀이
문제 : 보호소에서 중성화한 동물 이름
문제핵심
특정 문자열을 포함하는 데이터 조회하기
조건
ANIMAL_INS 테이블의 SEX_UPON_INTAKE 열이 'Intact' 이면서
ANIMAL_OUTS 테이블의 SEX_UPON_OUTCOME 열이 'Neutered' 거나 'Spayed' 인
데이터를 ANIAL_ID 기준 오름차순 정렬
WHERE 절에서만 쿼리를 잘 짜면 쉽다
Neutered 와 Spayed 의 조건을 설정할 때, LIKE 를 사용하면 두 줄을 써야하기 때문에
REGEXP 를 사용해서 코드를 조금 짧게 만들었다
SELECT
i.ANIMAL_ID,
i.ANIMAL_TYPE,
i.NAME
FROM
ANIMAL_INS i INNER JOIN ANIMAL_OUTS o
ON i.ANIMAL_ID = o.ANIMAL_ID
WHERE
i.SEX_UPON_INTAKE LIKE 'Intact%'
AND o.SEX_UPON_OUTCOME REGEXP 'Spayed|Neutered'
ORDER BY
i.ANIMAL_ID ASC;
문제 : 식품 분류별 가장 비싼 식품의 정보 조회하기
문제핵심
그룹 별 최댓값을 가지는 케이스를 어떻게 불러와야하는지
조건
카테고리가 과자, 국, 김치, 식용유인 케이스만 조회
CATEGORY 별로 가장 비싼 제품을 조회
가격별로 내림차순 정렬
풀이흐름
첫번째 코드 :
CTE에서 윈도우 함수로 CATEGORY 별 최대가격을 기록한 열 MAX_PRICE를 생성하고
메인쿼리에서 PRICE 가 MAX_PRICE 인 케이스만 조회하면 될 것이라고 생각했다
간단한 방법이지만 특정한 상황에서는 의도한 것과 다른 결과를 불러올 것 같아서
추천되지 않는다
WITH TBL AS (
SELECT
CATEGORY,
PRODUCT_NAME,
PRICE,
MAX(PRICE) OVER (PARTITION BY CATEGORY) AS MAX_PRICE
FROM
FOOD_PRODUCT
WHERE
CATEGORY IN ('과자', '국', '김치', '식용유')
)
SELECT
CATEGORY,
MAX_PRICE,
PRODUCT_NAME
FROM
TBL
WHERE
PRICE = MAX_PRICE
ORDER BY
PRICE DESC;
두번째 코드 :
WHERE 절에서 서브쿼리를 생성해서 조건에 맞는 CATEGORY와 PRICE 쌍을 불러왔다
그러나 메인쿼리의 FROM 에서 한번, WHERE의 서브쿼리에서 한번 데이터를 스캔하게 되는 것 같아서
데이터의 크기가 크면 조금 느려질 것 같다
SELECT
CATEGORY,
PRICE AS MAX_PRICE,
PRODUCT_NAME
FROM
FOOD_PRODUCT
WHERE
(CATEGORY,PRICE) IN (
SELECT CATEGORY, MAX(PRICE)
FROM FOOD_PRODUCT
WHERE CATEGORY IN ('과자', '국', '김치', '식용유')
GROUP BY CATEGORY
)
ORDER BY
PRICE DESC;
세번째 코드:
JOIN 을 사용해서 데이터를 두번 스캔하는 일이 없도록 작성했다
JOIN을 사용해도 결국 두개의 테이블을 스캔하긴하지만 이 방법이 더 빠르다고 한다(GPT가 말하길..)
이 방법도 CATEGORY와 PRICE 쌍을 기준으로 불러오는 점에서 두번째 코드와 동일하다
앞으로 이 방법을 애용해야겠다
SELECT
f.CATEGORY,
t.MAX_PRICE,
f.PRODUCT_NAME
FROM
FOOD_PRODUCT f
INNER JOIN (
SELECT CATEGORY, MAX(PRICE) AS MAX_PRICE
FROM FOOD_PRODUCT
WHERE CATEGORY IN ('과자', '국', '김치', '식용유')
GROUP BY CATEGORY
) AS t
ON f.CATEGORY = t.CATEGORY
AND f.PRICE = t.MAX_PRICE
ORDER BY
PRICE DESC;
문제 : 5월 식품들의 총매출 조회하기
문제핵심
적절히 그룹별 연산 활용하기
조건
2022년 5월 데이터만 조회
제품 별 총 매출 열을 생성
총매출 기준 내림차순, 제품ID 기준 오름차순 정렬
풀이흐름
두 개의 테이블을 내부조인한다
WHERE 절에서 2022년 5월 데이터로 제한한다
제품 별로 그룹화해서 가격과 판매량을 곱해서 합한 열을 생성한다
문제의 나머지 조건에 맞춰서 정렬한다
SELECT
o.PRODUCT_ID,
p.PRODUCT_NAME,
SUM(o.AMOUNT * p.PRICE) AS TOTAL_SALES
FROM
FOOD_PRODUCT p INNER JOIN FOOD_ORDER o
ON p.PRODUCT_ID = o.PRODUCT_ID
WHERE
o.PRODUCE_DATE REGEXP '2022-05'
GROUP BY
o.PRODUCT_ID
ORDER BY
TOTAL_SALES DESC,
PRODUCT_ID ASC;
문제 : 취소되지 않은 진료 예약 조회하기
문제핵심
문제 잘 읽고 조건 잘 보기
조건
흉부외과(CS) 진료만 조회
2022-04-13 일 예약만 조회
취소되지 않은 예약만 조회
진료예약날짜 기준 오름차순 정렬
풀이흐름
SELECT 하라는 열도 많고 조건이 좀 많아보이지만
서브쿼리도, 그룹화 같은 것도 필요없다
문제에 써있는대로만 조건 추가하고 조회하면된다
4단계스럽지 않은 문제
SELECT
a.APNT_NO,
p.PT_NAME,
p.PT_NO,
a.MCDP_CD,
d.DR_NAME,
a.APNT_YMD
FROM
APPOINTMENT a
INNER JOIN DOCTOR d ON a.MDDR_ID = d.DR_ID
INNER JOIN PATIENT p ON a.PT_NO = p.PT_NO
WHERE
a.APNT_CNCL_YN = 'N' AND
a.APNT_YMD REGEXP '2022-04-13' AND
a.MCDP_CD REGEXP 'CS'
ORDER BY
a.APNT_YMD ASC;
문제 : 서울에 위치한 식당 목록 출력하기
문제핵심
정확하게 조건 명시하기, 조회할 열 파악하기?
조건
서울에 있는 가게만 조회
가게 별 평균 리뷰점수 열 생성
평균리뷰점수 열 소수점 세째자리에서 반올림
평균점수 기준 내림차순, 즐겨찾기 기준 내림차순 정렬
문제흐름
선택해야할 열들을 자세히 보면 그룹화를 어떻게 해야할지 감이 잡힌다
평균 리뷰점수 열을 제외하면
REST_ID 별로 NAME,TYPE,FAVORITES,ADDRESS는 하나밖에 없으니
GROUP BY에서 REST_ID로 그룹화를 하면 된다
WHERE 절에서는 '서울' 로 시작하는 주소들을 필터링 해준다
(REGEXP '서울') 을 사용하거나 (LIKE '%서울%') 을 사용하면 오답이다
서울의 가게가 아닌데 '서울' 이라는 글자가 주소 중간에 들어갈 수 있기 때문이다
그리고 문제의 나머지 조건에 맞춰서 정렬 및 열 선택을 한다
SELECT
i.REST_ID,
i.REST_NAME,
i.FOOD_TYPE,
i.FAVORITES,
i.ADDRESS,
ROUND(AVG(r.REVIEW_SCORE), 2) AS SCORE
FROM
REST_INFO i INNER JOIN REST_REVIEW r
ON i.REST_ID = r.REST_ID
WHERE
i.ADDRESS LIKE '서울%'
GROUP BY
i.REST_ID
ORDER BY
SCORE DESC,
FAVORITES DESC;
문제 : 저자 별 카테고리 별 매출액 집계하기
문제핵심
그룹 별 연산 정확하게 하기
조건
2022년 1월 데이터만 조회
저자 별, 카테고리 별 매출액을 기록한 열 생성
작가ID 오름차순, 카테고리 내림차순 정렬
풀이흐름
먼저 최종 SELECT 에 필요한 열을 확인하고 테이블들을 조인한다
최종 조회할 열과 그룹화할 열들을 잘 살펴보면 거의 동일하다
그냥 GROUP BY로 ID와 CATEGOY 별로 그룹화 한다
판매가 x 판매량을 모두 합해서 새로운 열 TOTAL_SALES 을 생성
WHERE 에서 판매 날짜를 2022년 1월로 제한
문제의 나머지 조건에 맞춰서 정렬 및 열 선택
SELECT
author.AUTHOR_ID,
author.AUTHOR_NAME,
book.CATEGORY,
SUM(sales.SALES * book.PRICE) AS TOTAL_SALES
FROM
BOOK book
INNER JOIN AUTHOR author ON book.AUTHOR_ID = author.AUTHOR_ID
INNER JOIN BOOK_SALES sales ON book.BOOK_ID = sales.BOOK_ID
WHERE
sales.SALES_DATE LIKE '2022-01%'
GROUP BY
author.AUTHOR_ID,
book.CATEGORY
ORDER BY
author.AUTHOR_ID ASC,
book.CATEGORY DESC;
문제 : 년, 월, 성별 별 상품 구매 회원 수 구하기
문제핵심
문제 제대로 읽기..
조건
연, 월, 성별 기준으로 그룹화해서 회원 수를 기록한 열 생성
연,월, 성별 기준 오름차순 정렬
풀이흐름
최종적으로 필요한 열을 확인하고 테이블을 조인한다
연,월을 추출할때는 YEAR() 와 MONTH() 함수를 사용하면 된다
그리고 연,월,성별 기준으로 그룹화한 뒤에 COUNT() 를 사용하면 된다
그러나 여기서 같은 연,월에 여러번 구매한 회원이 있다면 이 케이스들도 모두 조회되기 때문에
COUNT() 안에 DISTINCT를 사용해서 중복된 회원은 집계하지 않도록 한다
문제의 나머지 조건에 맞춰서 정렬 및 열 선택을 한다
SELECT
YEAR(sales.SALES_DATE) AS YEAR,
MONTH(sales.SALES_DATE) AS MONTH,
info.GENDER,
COUNT(DISTINCT sales.USER_ID) AS USERS
FROM
ONLINE_SALE sales
INNER JOIN USER_INFO info ON sales.USER_ID = info.USER_ID
WHERE
info.GENDER IS NOT NULL
GROUP BY
YEAR(sales.SALES_DATE),
MONTH(sales.SALES_DATE),
info.GENDER
ORDER BY
YEAR(sales.SALES_DATE) ASC,
MONTH(sales.SALES_DATE) ASC,
info.GENDER ASC;
문제 : 우유와 요거트가 담긴 장바구니
문제핵심
그룹 집계 및 불필요한 데이터 제외
조건
milk와 yogurt가 모두 담긴 장바구니 조회
장바구니 ID 오름차순 정렬
풀이흐름
먼저 WHERE에서 milk 나 yogurt를 구매한 카트 ID 만 필터링한다
GROUP BY로 CART_ID 별로 그룹화 한다
HAVING 에서 DISTINCT를 사용해서 그룹별 NAME의 고유 카운트 수가 2개인 케이스만 조회한다
그러면 milk 와 yogurt를 모두 구매한 카트만 남게된다
SELECT
CART_ID
FROM
CART_PRODUCTS
WHERE
NAME REGEXP 'Milk|Yogurt'
GROUP BY
CART_ID
HAVING
COUNT(DISTINCT NAME) >= 2
문제 : 주문량이 많은 아이스크림들 조회하기
문제핵심
목표에 맞게 정확하게 테이블 합치기
조건
7월 , 상반기 테이블의 맛 별 총판매량 열을 생성,
판매량 많은 순서대로 3개의 케이스만 조회
풀이흐름
FLAVOR 기준으로 조인해서 두 테이블을 조인하는게 좋을수도 있다
그러나 조인을 하게 되면 7월이나 상반기에 테이블이 하나라도 없으면
조인이 되지 않아 결과에서 제외되기 때문에
UNION ALL 을 사용했다
FLAVOR가 중복된 것을 피하기 위해 FLAVOR 별 TOTAL_ORDER 합을 구해놓고
UNION ALL 로 합친 뒤에
합친 TOTAL_ORDER 순으로 내림차순 정렬 했다
WITH TBL AS (
SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
FROM FIRST_HALF
GROUP BY FLAVOR
UNION ALL
SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
FROM JULY
GROUP BY FLAVOR
)
SELECT FLAVOR
FROM TBL
GROUP BY FLAVOR
ORDER BY SUM(TOTAL_ORDER) DESC
LIMIT 3;
문제 : 연간 평가점수에 해당하는 평가 등급 및 성과금 조회하기
문제핵심
GROUP BY 잘 이해하기?
조건
회원 별 평균점수를 구간별로 나눈 열 생성
외원 별 평균점수를 구간으로 나누어 보너스 열 생성
회원 ID 오름차순 정렬
풀이흐름
회원별로 평균 점수를 구하고 평균점수에 따라 등급과 보너스 열을 생성할 것
CASE 에서 매 조건마다 AVG(SCORE) >= n 처럼 작성하면
평균계산을 여러번 하기 때문에 뭔가 비효율적이다
그래서 CTE 로 회원별 평균 점수 AVG_SCORE를 기록한 테이블을 생성한 뒤에
메인 쿼리에서 조인한 뒤에 CASE 에서 사용해서 등급과 보너스 열을 생성했다
문제의 나머지 조건에 맞춰서 정렬 및 열 선택을 하면된다
WITH TBL AS (
SELECT EMP_NO, AVG(SCORE) AS AVG_SCORE
FROM HR_GRADE
GROUP BY EMP_NO
)
SELECT
e.EMP_NO,
e.EMP_NAME,
CASE
WHEN t.AVG_SCORE >= 96 THEN 'S'
WHEN t.AVG_SCORE >= 90 THEN 'A'
WHEN t.AVG_SCORE >= 80 THEN 'B'
ELSE 'C'
END AS GRADE,
CASE
WHEN t.AVG_SCORE >= 96 THEN e.SAL * 0.2
WHEN t.AVG_SCORE >= 90 THEN e.SAL * 0.15
WHEN t.AVG_SCORE >= 80 THEN e.SAL * 0.1
ELSE 0
END AS BONUS
FROM
HR_EMPLOYEES e
INNER JOIN TBL t ON e.EMP_NO = t.EMP_NO
GROUP BY
t.EMP_NO
문제 : 그룹별 조건에 맞는 식당 목록 출력하기
문제핵심
그룹 별 최댓값을 가진 케이스들 조회하기
조건
리뷰를 가장 많이 작성한 회원들의 정보 조회
작성일 기준 오름차순, 작성 텍스트 기준 오름차순 정렬
풀이흐름
먼저 ID 별 리뷰 수를 기록한 CTE1을 생성한다
CTE1를 활용해서 가장 많은 리뷰수가 기록된 CTE2를 생성한다
메인쿼리에서 필요한 테이블과 CTE 들을 조인한다
문제의 나머지 조건에 맞게 정렬 및 열 선택을 한다
WITH CTE1 AS (
SELECT MEMBER_ID, COUNT(*) AS REVIEW_COUNT
FROM REST_REVIEW
GROUP BY MEMBER_ID),
CTE2 AS (
SELECT MAX(REVIEW_COUNT) AS MAX_COUNT
FROM CTE1)
SELECT
member.MEMBER_NAME,
review.REVIEW_TEXT,
DATE_FORMAT(review.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM
CTE1
INNER JOIN CTE2 ON CTE1.REVIEW_COUNT = CTE2.MAX_COUNT
LEFT JOIN REST_REVIEW review ON CTE1.MEMBER_ID = review.MEMBER_ID
LEFT JOIN MEMBER_PROFILE member ON CTE1.MEMBER_ID = member.MEMBER_ID
ORDER BY
REVIEW_DATE ASC,
review.REVIEW_TEXT ASC;
문제 : 입양 시각 구하기(2)
문제핵심
재귀함수 사용하기
조건
시간 별로 입양 수 열 생성
입양수가 0이어도 시간은 나타나야함
시간대 순 정렬
풀이흐름
재귀 CTE를 사용해서 모든 시간대가 기록된 행을 만든다
메인쿼리에서 시간별 입양수를 기록한 서브쿼리와 CTE를 조인한다
COALESCE로 결측치는 0으로 바꿔준다
문제의 조건에 맞춰 정렬 및 열을 선택한다
WITH RECURSIVE NUM AS (
SELECT 0 AS HOUR
UNION ALL
SELECT HOUR + 1 FROM NUM
WHERE HOUR < 23
)
SELECT
n.HOUR,
COALESCE(t.DATE_COUNT,0) AS COUNT
FROM
NUM n
LEFT JOIN (
SELECT HOUR(DATETIME) AS HOUR, COUNT(*) DATE_COUNT
FROM ANIMAL_OUTS
GROUP BY HOUR(DATETIME)) t
ON n.HOUR = t.HOUR
ORDER BY
HOUR ASC;
문제 : 특정 세대의 대장균 찾기
문제 핵심
조인에 대한 적당한 이해
조건
3번째 세대의 대장균만 조회
ID 오름차순 정렬
풀이흐름
첫번째 코드 :
세대가 기록된 열을 만들고 3세대만 조회할 예정,
재귀 CTE로 첫세대(부모세대가 NULL)들을 선택하고
1 값을 가지는 열 GENERATION을 만든다(1세대기 때문)
그 다음으로 다음 세대에 대한 케이스를 추가하기 위해 UNION ALL을 해준다
재귀 CTE로 추가 되는 행은
CTE의 ID와 ECOLI_DATA 테이블의 PARENT_ID를 INNER JOIN 을 하면
ECOLI_DATA의 ID는 2세대 열이 된다.
동시에 GENERATION + 1을 해서 2세대임으로 알려준다
재귀 CTE의 종료 조건을 명시할 필요는 없다
새로운 데이터가 생성되지 않으면 자동으로 종료되기 때문이다
이후 메인쿼리에서 CTE를 불러와서 3세대만 선택한다
WITH RECURSIVE TBL AS (
SELECT
ID,
PARENT_ID,
1 AS GENERATION
FROM
ECOLI_DATA
WHERE
PARENT_ID IS NULL
UNION ALL
SELECT
e.ID,
e.PARENT_ID,
GENERATION + 1
FROM
ECOLI_DATA e INNER JOIN TBL t
ON e.PARENT_ID = t.ID
)
SELECT ID
FROM TBL
WHERE GENERATION = 3
ORDER BY ID ASC;
두번째 코드 :
ECOLI_DATA 테이블에서 부모세대가 NULL인 케이스의 ID와
셀프 조인할 테이블의 PARENT_ID를 조인하면
기존 테이블의 ID는 1세대, 셀프조인한 테이블의 ID는 2세대가 된다
이런 방식으로 2번 셀프 조인을 하면 마지막으로 조인한 테이블의 ID가
3세대가 된다
SELECT
c.ID
FROM (
SELECT ID,PARENT_ID FROM ECOLI_DATA WHERE PARENT_ID IS NULL) a
INNER JOIN ECOLI_DATA b ON a.ID = b.PARENT_ID
INNER JOIN ECOLI_DATA c ON b.ID = c.PARENT_ID
ORDER BY
c.ID ASC;
문제 : 자동차 대여 기록 별 대여 금액 구하기
문제 핵심
적당한 조인 기준 열 생성하기?
조건
트럭 데이터만 조회,
대여금, 대여기록, 할인타입에 맞춰 최종 대여금 계산,
대여금, 대여기록 내림차순 정렬
풀이흐름
1. CTE로 HISTORY 테이블에서 다음의 열들을 불러온다
HISTORY_ID (식별 열)
CAR_ID (CAR 테이블과 조인용)
대여기간 (대여금 계산용)
할인타입 (DISCOUNT 테이블과 조인용)
2. CTE로 DISCOUNT 테이블에서 트럭 종류의 케이스만 불러온다
나중에 할인타입 열을 기준으로 조인을 하게 되는데
트럭외에 다른 자동차 종류가 있으면
중복 열이 생기게 된다
3. 메인 쿼리에서 조인한다
대여기록 테이블, 자동차정보 테이블 LEFT JOIN (정렬 기준 CAR_ID)
대여기록 테이블, 할인정보 테이블 LEFT JOIN (조인 기준 DURATION_TYPE)
4. SELECT 에서 (대여금 * 할인율 * 대여기간) 계산
CASE로 대여기간이 7일 미만이라서 '할인없음' 에 해당하는 케이스는
(대여금 * 대여기간) 만 계산해준다
5. 문제의 나머지 조건에 맞춰서 정렬
WITH history AS (
SELECT
HISTORY_ID, CAR_ID, (DATEDIFF(END_DATE, START_DATE) + 1) AS DATE_DIFF,
CASE
WHEN (DATEDIFF(END_DATE, START_DATE) + 1) >= 90 THEN '90일 이상'
WHEN (DATEDIFF(END_DATE, START_DATE) + 1) >= 30 THEN '30일 이상'
WHEN (DATEDIFF(END_DATE, START_DATE) + 1) >= 7 THEN '7일 이상'
ELSE '할인없음'
END AS DURATION_TYPE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY),
discount AS (
SELECT CAR_TYPE, DURATION_TYPE, DISCOUNT_RATE
FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN
WHERE CAR_TYPE = '트럭')
SELECT
h.HISTORY_ID,
CASE
WHEN h.DURATION_TYPE = '할인없음'
THEN FLOOR(c.DAILY_FEE * h.DATE_DIFF)
ELSE FLOOR((c.DAILY_FEE * (1 - 0.01 * d.DISCOUNT_RATE) * h.DATE_DIFF))
END AS FEE
FROM
history h
LEFT JOIN CAR_RENTAL_COMPANY_CAR c
ON h.CAR_ID = c.CAR_ID
LEFT JOIN discount d
ON h.DURATION_TYPE = d.DURATION_TYPE
WHERE
c.CAR_TYPE = '트럭'
ORDER BY
FEE DESC,
h.HISTORY_ID DESC;
문제 : Front End 개발자 찾기
문제핵심
& 연산자 사용법을 알고 있는지
조건
Front End 개발자의 정보만 조회
ID 오름차순 정렬
풀이흐름
이 문제는 & 연산자를 모르면 코드가 참 아쉬워진다
& 연산자는 두 정수를 이진수로 만든 뒤에 비교해서
같은 자리에 둘다 1일때만 1을, 아니면 0을 반환한다
그래서 Front End 분야의 스킬 코드를 모두 합하고
개발자들의 스킬코드와 & 연산자로 비교했을때
0이 아닌 양수이면 해당 케이스는 Front End 개발자인 것이다
CTE나 서브쿼리를 통해 Front End 개발자의 스킬 코드 합을 계산해주고
개발자들의 Skill Code 와 &로 비교하면 된다
CTE 사용 풀이
WITH TBL AS (
SELECT SUM(CODE) AS SUM_CODE
FROM SKILLCODES
WHERE CATEGORY = 'Front End')
SELECT
d.ID,
d.EMAIL,
d.FIRST_NAME,
d.LAST_NAME
FROM
DEVELOPERS d INNER JOIN TBL t
ON d.SKILL_CODE & t.SUM_CODE > 0
ORDER BY
d.ID ASC;
CTE 없이 풀이
SELECT
ID,
EMAIL,
FIRST_NAME,
LAST_NAME
FROM
DEVELOPERS
WHERE
SKILL_CODE & (
SELECT SUM(CODE)
FROM SKILLCODES
WHERE CATEGORY = 'Front End'
) > 0
ORDER BY
ID ASC;
문제 : 특정 기간동안 대여 가능한 자동차들의 대여비용 구하기
문제핵심
반례 없이 조건 만족하기
조건
'세단', 'SUV' 차량만 조회
2022년 11월에 대여가능한 차량만 조회
30일 대여금액이 50~200만 사이인 차량만 조회
할인적용된 30일 대여금 열 생성
대여금액 내림차순, 차종류 오름차순, 차ID 내림차순 정렬
풀이흐름
1.차종류 제한 조건 충족
메인쿼리에서 사용할 CTE를 생성한다 (CTE를 사용하지 않고 FROM 그대로 작성해도 된다)
차량정보 테이블과 할인정보 테이블을 CAR TYPE 기준 조인을 한다
이 때 CAR TYPE 별로 여러가지 할인이 있기 때문에
중복 케이스가 생성된다 하지만 문제에서는
30일 기준 할인율만 구하면 되기 때문에 할인종류를 '30일 이상' 으로 제한한다
또한 차 종류도 문제의 조건에 맞게 '세단', 'SUV'로 제한한다
그러면 이 CTE는 [차량ID, 차종류, 대여금, 30일 할인율] 열을 가진
테이블이 된다 (즉, 필수적인 차량 정보만 가진 CTE 생성)
2. 30일 대여금이 50~200만 이고 11월에 대여가능한 차량 조건 충족
메인쿼리의 WHERE 에서 2가지 조건을 설정한다
- WHERE 절에서 (대여금 x 할인율 x 30일) 이
50만~200만 구간 안에 있는 케이스만 조회하는 조건을 설정
- 서브쿼리를 생성하여 11월에 대여가능한 차량의 ID로 제한
여기서 '11월에 대여가능한 차량' 을 추출하기보단
'11월에 대여중인 차량' 을 제외하는 것이 좋다
나는 NOT IN 을 사용했다
3. 메인쿼리에서 나머지 조건 충족
FROM 에서 만들어둔 CTE를 가져온다
SELECT 에서 문제가 요구한 열과
30일 대여금을 계산한 열을 선택한다
ORDER BY 에서 문제의 조건에 맞게 정렬한다
WITH CAR_INFO AS (
SELECT
car.CAR_ID,
car.CAR_TYPE,
car.DAILY_FEE,
discount.DISCOUNT_RATE
FROM
CAR_RENTAL_COMPANY_CAR car
INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN discount
ON car.CAR_TYPE = discount.CAR_TYPE
WHERE
discount.DURATION_TYPE = '30일 이상' AND car.CAR_TYPE IN ('세단','SUV')
)
SELECT
c.CAR_ID,
c.CAR_TYPE,
FLOOR(c.DAILY_FEE * (1 - (c.DISCOUNT_RATE/100)) * 30) AS FEE
FROM
CAR_INFO c
WHERE
c.DAILY_FEE * (1 - (c.DISCOUNT_RATE/100)) * 30 BETWEEN 500000 AND 2000000
AND
c.CAR_ID NOT IN (
SELECT CAR_ID
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE NOT (START_DATE > '2022-11-30' OR END_DATE < '2022-11-01'))
ORDER BY
FEE DESC,
c.CAR_TYPE ASC,
c.CAR_ID DESC;
문제 : 언어별 개발자 분류하기
문제핵심
비트연산자 활용과 CASE의 적절한 활용
조건
개발자의 스킬코드 별로
Front End 스킬 중하나와 Python 스킬을 가지고 있으면 A
C# 스킬을 가지고 있으면 B
Front End 스킬 중 하나를 가지고 있으면 C
로 GRADE를 만들어서
GRADE가 있는 개발자들을 조회
GRADE, ID 오름차순 정렬
풀이흐름
CASE를 사용해서 GRADE열을 생성해야한다
그러기 위해선 GRADE 별 조건에 맞는 스킬코드 합을 계산하고
& 연산자로 비교해서 GRADE를 부여하는 것이 좋아보인다
GRADE가 없는 개발자는 NULL을 지정해준다
NULL을 나중에 제외해야 하는데
메인쿼리의 CASE 에서 GRADE를 지정하고
WHERE 에서 NULL 인 데이터를 제외하려고 하면
쿼리 작동 순서에 맞지 않아서 오류가 발생한다 ( 작동순서 WHERE -> SELECT)
그래서 서브쿼리나 CTE 를 사용해서 GRADE를 지정해놓고
메인쿼리에서 NULL을 제외하는 방법을 사용했다
(여기서는 CTE 사용함)
또한 CASE 의 조건마다 서브쿼리를 사용하면 연상량이 많아질 것으로 예상되므로
CTE를 하나 더 작성해서 GRADE 별 조건에 맞는 스킬코드 합을 미리 계산해두는 것이 좋아보인다
이후 문제의 나머지 조건에 맞춰 정렬 및 열을 선택해서 조회한다
(스킬 코드합을 미리 계산한 코드)
WITH CODE_SUM AS (
SELECT
(SELECT SUM(CODE) FROM SKILLCODES WHERE CATEGORY = 'Front End') AS FRONT_END,
(SELECT SUM(CODE) FROM SKILLCODES WHERE NAME = 'Python') AS PYTHON,
(SELECT SUM(CODE) FROM SKILLCODES WHERE NAME = 'C#') AS CSHARP
),
CTE AS (
SELECT
CASE
WHEN SKILL_CODE & c.FRONT_END > 0 AND SKILL_CODE & c.PYTHON > 0 THEN 'A'
WHEN SKILL_CODE & c.CSHARP > 0 THEN 'B'
WHEN SKILL_CODE & c.FRONT_END > 0 THEN 'C'
END AS GRADE,
ID, EMAIL
FROM DEVELOPERS, CODE_SUM c
)
SELECT *
FROM CTE
WHERE GRADE IS NOT NULL
ORDER BY GRADE, ID;
(스킬 코드합을 미리 계산해두지 않은 코드)
WITH CTE AS (
SELECT
CASE
WHEN SKILL_CODE & (SELECT SUM(CODE) FROM SKILLCODES WHERE CATEGORY = 'Front End') > 0
AND SKILL_CODE & (SELECT SUM(CODE) FROM SKILLCODES WHERE NAME = 'Python') > 0
THEN 'A'
WHEN SKILL_CODE & (SELECT SUM(CODE) FROM SKILLCODES WHERE NAME = 'C#') > 0
THEN 'B'
WHEN SKILL_CODE & (SELECT SUM(CODE) FROM SKILLCODES WHERE CATEGORY = 'Front End') > 0
THEN 'C'
ELSE NULL
END AS GRADE,
ID, EMAIL
FROM
DEVELOPERS
)
SELECT *
FROM CTE
WHERE GRADE IS NOT NULL
ORDER BY GRADE ASC, ID ASC;
'SQL 문제풀이' 카테고리의 다른 글
프로그래머스 SQL 5단계 문제풀이 (0) | 2025.03.28 |
---|---|
프로그래머스 SQL 3단계 문제풀이 (2) | 2025.03.17 |