생각보다 양이 많네.....
https://school.programmers.co.kr/learn/courses/30/parts/17044
프로그래머스
SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
1. 대여 횟수가 많은 자동차들의 월별 대여 횟수 구하기
SELECT
DATE_FORMAT(START_DATE, '%m') AS MONTH,
CAR_ID,
COUNT(*) AS RECORDS
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE START_DATE BETWEEN '2022-08-01' AND '2022-10-31'
AND CAR_ID IN (
-- 5회 이상 대여된 자동차 ID만 필터링
SELECT CAR_ID
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE START_DATE BETWEEN '2022-08-01' AND '2022-10-31'
GROUP BY CAR_ID
HAVING COUNT(*) >= 5
)
GROUP BY MONTH, CAR_ID
ORDER BY MONTH ASC, CAR_ID DESC;
- START_DATE를 기준으로 구하고 있으므로 WHERE 조건 절에 START_DATE를 기준으로 날짜 사이에 대여한 것들을 필터링한다. 또한 CAR_ID를 기준으로 그룹화 하여 HAVING 조건을 붙인다.
- 이렇게 필터링 된 정보들을 MONTH와 CAR_ID로 GROUP BY 한다.
- GROUP BY에 데이터를 두 개 이상 넣는 경우 MONTH 그룹화 후 그 안에서 CAR_ID로 그룹화하게 된다.
- 비집계함수인 경우, 반드시 GROUP BY에 병시된 칼럼만 SELECT에서 사용할 수 있다! ( sql_mode=only_full_group_by 오류)
2. 자동차 대여 기록에서 대여중/대여 가능 여부 구분하기
SELECT
CAR_ID,
CASE
WHEN MAX(CASE WHEN '2022-10-16' BETWEEN START_DATE AND END_DATE THEN 1 ELSE 0 END) = 1
THEN '대여중'
ELSE '대여 가능'
END AS AVAILABILITY
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY CAR_ID
ORDER BY CAR_ID DESC;
- GROUP BY로 묶은 후 조건 검사 시 GROUP 하나로 묶인 모든 데이터를 검사한다.
- 단, 하나라도 대여중인 결과가 나오면 대여중으로 표시해야 하므로 MAX() 를 이용해 검사한다.
3. 고양이와 개는 몇 마리 있을까
-- 코드를 입력하세요
SELECT ANIMAL_TYPE, COUNT(*) AS count FROM ANIMAL_INS GROUP BY ANIMAL_TYPE ORDER BY ANIMAL_TYPE;
- CAT을 먼저 오도록 정렬하기 위해 알파벳순으로 ORDER BY했다. 만약 데이터 순서를 명시적으로 작성하고 싶으면 ORDER BY FIELD(ANIMAL_TYPE, 'Dog', 'Cat', 'Rabbit', 'Other'); 와 같이 FIELD() 함수 이용하기
4. 동명 동물 수 찾기
-- 코드를 입력하세요
SELECT NAME, COUNT(*) AS COUNT FROM ANIMAL_INS WHERE NAME IS NOT NULL GROUP BY NAME HAVING COUNT(*)>=2 ORDER BY NAME;
5. 년, 월, 성별 별 구매 회원 수 구하기
-- 코드를 입력하세요
SELECT YEAR(OS.SALES_DATE) AS YEAR, MONTH(OS.SALES_DATE) AS MONTH, UI.GENDER, COUNT(DISTINCT OS.USER_ID) AS USERS FROM USER_INFO UI JOIN ONLINE_SALE OS ON UI.USER_ID=OS.USER_ID
WHERE GENDER IS NOT NULL
GROUP BY YEAR(OS.SALES_DATE), MONTH(OS.SALES_DATE), UI.GENDER
ORDER BY YEAR(OS.SALES_DATE), MONTH(OS.SALES_DATE), UI.GENDER;
- 문제 주의!! 총 구매수가 아닌 회원의 수를 구해야 한다. 따라서 USER_ID를 DISTINCT 한 갯수를 SELECT 해야 한다.
6. 자동차 종류 별 특정 옵션이 포함된 자동차 수 구하기
-- 코드를 입력하세요
SELECT CAR_TYPE, COUNT(*) AS CARS FROM CAR_RENTAL_COMPANY_CAR WHERE OPTIONS LIKE "%가죽시트%" OR OPTIONS LIKE "%열선시트%" OR OPTIONS LIKE "%통풍시트%" GROUP BY CAR_TYPE ORDER BY CAR_TYPE;
- WHERE 절이 LIKE ~ OR을 여러개 쓰면서 지저분해졌다. 그러나 LIKE 조건을 IN으로는 표현할 수 없음.
SELECT CAR_TYPE, COUNT(*) AS CARS
FROM CAR_RENTAL_COMPANY_CAR
WHERE OPTIONS REGEXP '가죽시트|열선시트|통풍시트'
GROUP BY CAR_TYPE
ORDER BY CAR_TYPE;
- 정규표현식 REGEXP 을 이용한 방식이다.
7. 성분으로 구분한 아이스크림 총 주문량
-- 코드를 입력하세요
SELECT INGREDIENT_TYPE, SUM(TOTAL_ORDER) AS TOTAL_ORDER FROM FIRST_HALF AS FH JOIN ICECREAM_INFO AS II ON FH.FLAVOR=II.FLAVOR
GROUP BY INGREDIENT_TYPE
ORDER BY TOTAL_ORDER;
- 정렬은 SELECT 절에 명시된 별칭으로도 가능하다.
8. 진료과별 총 예약 횟수 출력하기
-- 코드를 입력하세요
SELECT MCDP_CD AS "진료과코드", COUNT(*) AS "5월예약건수" FROM APPOINTMENT WHERE APNT_YMD BETWEEN "2022-05-01" AND "2022-05-31"
GROUP BY MCDP_CD ORDER BY 2, 1;
- DATE TYPE이랑 문자열 비교 안됨. BETWEEN 이용하기.
- 정렬은 SELECT 절에 명시된 컬럼 순서 숫자로도 가능
9. 입양 시각 구하기(1)
-- 코드를 입력하세요
SELECT HOUR(DATETIME) AS HOUR, COUNT(*) AS COUNT FROM ANIMAL_OUTS
WHERE HOUR(DATETIME) BETWEEN "9" AND "19" GROUP BY HOUR(DATETIME) ORDER BY 1;
- HOUR() 함수 사용!
10. 입양 시각 구하기(2)
WITH RECURSIVE TIME AS(
SELECT 0 AS HOUR
UNION ALL
SELECT HOUR + 1
FROM TIME
WHERE HOUR < 23
)
SELECT T.HOUR, COUNT(ANIMAL_ID) AS COUNT
FROM TIME T
LEFT JOIN (SELECT *, HOUR(DATETIME) AS HOUR
FROM ANIMAL_OUTS) O
ON T.HOUR = O.HOUR
GROUP BY 1
ORDER BY 1;
- ANIMAL_OUTS TABLE에 모든 시간대 정보가 없으므로, 0부터 23까지의 시간대 정보가 담긴 TABLE(혹은 쿼리)과 LEFT JOIN을 수행해야 한다.
- 재귀를 수행하지 않고 SELECT 0 AS HOUR UNION ALL ~~~ 이런식으로 모든 시간대를 직접 입력할 수 있지만, 코드가 못생겨지므로 안했다.
- TIME CTE를 수행하여 HOUR 컬럼의 값을 0부터 23까지 생성한다.이후 TIME CTE와 ANIMAl_OUTS를 LEFT JOIN하여 수행
11. 가격대 별 상품 개수 구하기
SELECT
FLOOR(PRICE / 10000) * 10000 AS PRICE_GROUP,
COUNT(*) AS PRODUCTS
FROM PRODUCT
WHERE PRICE >= 10000
GROUP BY PRICE_GROUP
ORDER BY PRICE_GROUP;
- FLOOR() 또는 DIV 연산자를 이용해 PRICE를 10000원대별로 구간을 나눈 후 그룹화하여 SELECT 한다.
12. 조건에 맞는 사원 정보 조회하기
-- 코드를 작성해주세요
SELECT SUM(SCORE) AS SCORE, HE.EMP_NO, HE.EMP_NAME, HE.POSITION, HE.EMAIL
FROM HR_DEPARTMENT HD
JOIN HR_EMPLOYEES HE ON HD.DEPT_ID = HE.DEPT_ID
JOIN HR_GRADE HG ON HE.EMP_NO = HG.EMP_NO
WHERE HG.YEAR="2022"
GROUP BY 2
ORDER BY 1 DESC
LIMIT 1;
- 3개 이상의 테이블을 JOIN하는 방식에 주목
13. 연간 평가 점수에 해당하는 평가 등급 및 성과금 조회하기
-- 코드를 작성해주세요
SELECT HE.EMP_NO, HE.EMP_NAME,
CASE WHEN AVG(HG.SCORE) >= 96 THEN "S"
WHEN AVG(HG.SCORE) >= 90 THEN "A"
WHEN AVG(HG.SCORE) >= 80 THEN "B"
ELSE "C"
END AS GRADE,
CASE WHEN AVG(HG.SCORE) >= 96 THEN HE.SAL*20/100
WHEN AVG(HG.SCORE) >= 90 THEN HE.SAL*15/100
WHEN AVG(HG.SCORE) >= 80 THEN HE.SAL*10/100
ELSE 0
END AS BONUS
FROM HR_DEPARTMENT HD
JOIN HR_EMPLOYEES HE ON HD.DEPT_ID = HE.DEPT_ID
JOIN HR_GRADE HG ON HE.EMP_NO = HG.EMP_NO
WHERE HG.YEAR="2022"
GROUP BY EMP_NO
ORDER BY 1;
- 조건이 너무 없어서....SCORE는 평균으로 계산해야한다.
14. 부서별 평균 연봉 조회하기
-- 코드를 작성해주세요
SELECT HD.DEPT_ID, HD.DEPT_NAME_EN, ROUND(AVG(HE.SAL)) AS AVG_SAL
FROM HR_DEPARTMENT HD
JOIN HR_EMPLOYEES HE ON HD.DEPT_ID = HE.DEPT_ID
GROUP BY HD.DEPT_ID
ORDER BY 3 DESC;
- 첫 째 자리에서 반올름은 ROUNT() 이용
15. 노선별 평균 역 사이 거리 조회하기
-- 코드를 작성해주세요
SELECT ROUTE, CONCAT(ROUND(SUM(D_BETWEEN_DIST), 1), 'km') TOTAL_DISTANCE, CONCAT(ROUND(AVG(D_BETWEEN_DIST), 2), 'km') AVERAGE_DISTANCE
FROM SUBWAY_DISTANCE GROUP BY ROUTE ORDER BY SUM(D_BETWEEN_DIST) DESC;
- ROUND(데이터, N) -> N으로 반올림이다.
- CONCAT()을 이용해 문자열 합산
- ORDER BY할 때 SELECT 에서 사용한 TOTAL_DISTANCE로 정렬하면 문자열을 기준으로 정렬하게 되므로 주의!
16. 물고기 종류 별 잡은 수 구하기
-- 코드를 작성해주세요
SELECT COUNT(*) FISH_COUNT, FNI.FISH_NAME FISH_NAME FROM FISH_INFO FI JOIN FISH_NAME_INFO FNI ON FI.FISH_TYPE=FNI.FISH_TYPE
GROUP BY FNI.FISH_NAME
ORDER BY 1 DESC;
17. 월별 잡은 물고기 수 구하기
-- 코드를 작성해주세요
SELECT COUNT(*) FISH_COUNT, MONTH(TIME) MONTH FROM FISH_INFO GROUP BY MONTH(TIME) ORDER BY 2;
18 . 특정 조건을 만족하는 물고기별 수와 최대 길이 구하기
SELECT COUNT(*) AS FISH_COUNT,
MAX(CASE WHEN LENGTH <= 10 THEN 10 ELSE LENGTH END) AS MAX_LENGTH,
FISH_TYPE
FROM FISH_INFO
GROUP BY FISH_TYPE
HAVING AVG(CASE WHEN LENGTH <= 10 THEN 10 ELSE LENGTH END) >= 33
ORDER BY FISH_TYPE;
- 바보같이 처음에 HAVING 안쓰고 WHRER이랑 서브쿼리 써서 풀었다. 왜그랬지?
- 문제 이해를 잘 못했는데, 평균을 구할 때 10 이하인 물고기들(LENGTH가 NULL)을 생각해서 계산하는 식을 작성해야 한다.(신경 안쓰면 테스트 1, 5번 틀림..)
19. 저자 별 카테고리 별 매출액 집계하기
-- 코드를 입력하세요
SELECT B.AUTHOR_ID, A.AUTHOR_NAME, B.CATEGORY, SUM(BS.SALES*B.PRICE) TOTAL_SALES
FROM BOOK B JOIN AUTHOR A ON B.AUTHOR_ID=A.AUTHOR_ID
JOIN BOOK_SALES BS ON B.BOOK_ID=BS.BOOK_ID
WHERE BS.SALES_DATE BETWEEN "2022-01-01" AND "2022-01-31"
GROUP BY B.AUTHOR_ID, B.CATEGORY
ORDER BY 1 ASC, 3 DESC;
- 같은 저자, 카테고리의 책이 여러권 있을 수 있으므로 SUM을 해준다.
- 참고로 MySQL에서 GROUP BY에 명시되지 않은 값은 SELECT에 사용할 수 없지만, 동일한 값이 없는 경우에는 사용가능함
20. 식품분류별 가장 비싼 식품의 정보 조회하기
SELECT FP.CATEGORY, FP.PRICE MAX_PRICE, FP.PRODUCT_NAME
FROM FOOD_PRODUCT FP
WHERE FP.CATEGORY IN ('과자', '국', '김치', '식용유')
AND FP.PRICE = (SELECT MAX(PRICE)
FROM FOOD_PRODUCT
WHERE CATEGORY = FP.CATEGORY)
ORDER BY FP.PRICE DESC;
- 서브쿼리 없이 바로 GROUP BY를 사용하면 MAX값은 맞게 출력되지만 PRODUCT_NAME은 해당 PRICE의 제품이 아닌 다른 제품(순서대로 아무거나..) 출력된다.
21. 카테고리 별 도서 판매량 집계하기
-- 코드를 입력하세요
SELECT B.CATEGORY, SUM(BS.SALES) FROM BOOK B JOIN BOOK_SALES BS ON B.BOOK_ID=BS.BOOK_ID
WHERE BS.SALES_DATE BETWEEN "2022-01-01" AND "2022-01-31"
GROUP BY B.CATEGORY
ORDER BY 1;
22. 즐겨찾기가 가장 많은 식당 정보 출력하기
SELECT RI.FOOD_TYPE, RI.REST_ID, RI.REST_NAME, RI.FAVORITES
FROM REST_INFO RI
WHERE RI.FAVORITES = (
SELECT MAX(FAVORITES)
FROM REST_INFO
WHERE FOOD_TYPE = RI.FOOD_TYPE
)
ORDER BY RI.FOOD_TYPE DESC;
23. 조건에 맞는 사용자와 총 거래금액 조회하기
-- 코드를 입력하세요
SELECT UGU.USER_ID, UGU.NICKNAME, SUM(UGB.PRICE) AS TOTAL_SALES
FROM USED_GOODS_BOARD UGB JOIN USED_GOODS_USER UGU ON UGB.WRITER_ID=UGU.USER_ID
WHERE UGB.STATUS = "DONE"
GROUP BY UGU.USER_ID
HAVING TOTAL_SALES >= 700000
ORDER BY 3;
24. 언어별 개발자 분류하기
WITH FrontEndCode AS (
SELECT SUM(CODE) AS CODE
FROM SKILLCODES
WHERE CATEGORY = 'Front End'
),
PythonCode AS (
SELECT CODE
FROM SKILLCODES
WHERE NAME = 'Python'
),
CSharpCode AS (
SELECT CODE
FROM SKILLCODES
WHERE NAME = 'C#'
)
SELECT
CASE
WHEN SKILL_CODE&FrontEndCode.CODE
AND SKILL_CODE&PythonCode.CODE THEN 'A'
WHEN SKILL_CODE&CSharpCode.CODE THEN 'B'
WHEN SKILL_CODE&FrontEndCode.CODE THEN 'C'
END AS GRADE,
ID, EMAIL
FROM DEVELOPERS, FrontEndCode, PythonCode, CSharpCode
HAVING GRADE IS NOT NULL
ORDER BY GRADE, ID;
- A, C조건을 모두 충족하는 경우가 있어 틀릴 수 있음
- FrontEndCode, PythonCode, CSharpCode 모두 중복적으로 서브쿼리를 계속 호출하므로 CTE를 만들어 주었다. 이것들을 SELECT에 사용하기 위해 FROM에서 호출하기
- 만약 SELECT 절에 사용한 데이터에 조건을 추가로 걸고 싶으면 WHERE이 아닌 HAVING 절에 작성한다.
회고
마지막 문제에서 진을 다 뺐다....
CTE에 익숙해 질수록 더 효율적인 코드가 나올 것 같다.
SQL은 문제 조건이 모호한게 많아서 더 헷갈린다.ㅜㅜ
'코딩테스트' 카테고리의 다른 글
[MySQL] 프로그래머스 SQL 고득점 Kit - JOIN (1) | 2025.02.22 |
---|---|
[MySQL] 프로그래머스 SQL 고득점 Kit - IS NULL (0) | 2025.02.21 |
[MySQL] 프로그래머스 SQL 고득점 Kit - SUM, MAX, MIN (0) | 2025.02.21 |
[MySQL] 프로그래머스 SQL 고득점 Kit - SELECT (0) | 2025.02.20 |
[Python] 백준 코테 연습 - DP (2) (0) | 2025.02.13 |