주문량이 많은 아이스크림들 조회하기
7월 아이스크림 총 주문량과 상반기의 아이스크림 총 주문량을 더한 값이 큰 순서대로 상위 3개의 맛을 조회하는 SQL 문을 작성해주세요.
-- Oracle
SELECT H.FLAVOR
FROM FIRST_HALF H
LEFT JOIN (
SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
FROM JULY
GROUP BY FLAVOR
) J
ON H.FLAVOR = J.FLAVOR
ORDER BY J.TOTAL_ORDER + H.TOTAL_ORDER DESC
FETCH FIRST 3 ROW ONLY;
-- MySQL
SELECT H.FLAVOR
FROM FIRST_HALF H
LEFT JOIN (
SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
FROM JULY
GROUP BY FLAVOR
) J
ON H.FLAVOR = J.FLAVOR
ORDER BY J.TOTAL_ORDER + H.TOTAL_ORDER DESC
LIMIT 3
특정 기간동안 대여 가능한 자동차들의 대여비용 구하기
CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블과 CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블에서 자동차 종류가 '세단' 또는 'SUV' 인 자동차 중 2022년 11월 1일부터 2022년 11월 30일까지 대여 가능하고 30일간의 대여 금액이 50만원 이상 200만원 미만인 자동차에 대해서 자동차 ID, 자동차 종류, 대여 금액(컬럼명: FEE) 리스트를 출력하는 SQL문을 작성해주세요. 결과는 대여 금액을 기준으로 내림차순 정렬하고, 대여 금액이 같은 경우 자동차 종류를 기준으로 오름차순 정렬, 자동차 종류까지 같은 경우 자동차 ID를 기준으로 내림차순 정렬해주세요.
NOT EXISTS
-- MySQL
SELECT C.CAR_ID, C.CAR_TYPE,
ROUND(C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) AS FEE
FROM CAR_RENTAL_COMPANY_CAR C
JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN P
ON P.CAR_TYPE = C.CAR_TYPE
AND P.DURATION_TYPE = '30일 이상'
WHERE C.CAR_TYPE IN ('세단', 'SUV')
AND NOT EXISTS (
SELECT 1
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H
WHERE H.CAR_ID = C.CAR_ID
AND H.START_DATE <= '2022-11-30'
AND H.END_DATE >= '2022-11-01'
)
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) >= 500000
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) < 2000000
ORDER BY FEE DESC, C.CAR_TYPE ASC, C.CAR_ID DESC;
-- Oracle
SELECT C.CAR_ID, C.CAR_TYPE,
ROUND(C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) AS FEE
FROM CAR_RENTAL_COMPANY_CAR C
JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN P
ON P.CAR_TYPE = C.CAR_TYPE
AND P.DURATION_TYPE = '30일 이상'
WHERE C.CAR_TYPE IN ('세단', 'SUV')
AND NOT EXISTS (
SELECT 1
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H
WHERE H.CAR_ID = C.CAR_ID
AND H.START_DATE <= DATE '2022-11-30'
AND H.END_DATE >= DATE '2022-11-01'
)
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) >= 500000
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) < 2000000
ORDER BY FEE DESC, C.CAR_TYPE ASC, C.CAR_ID DESC;
LEFT JOIN
-- MySQL
SELECT
C.CAR_ID,
C.CAR_TYPE,
ROUND(C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) AS FEE
FROM CAR_RENTAL_COMPANY_CAR C
JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN P
ON P.CAR_TYPE = C.CAR_TYPE
AND P.DURATION_TYPE = '30일 이상'
LEFT JOIN CAR_RENTAL_COMPANY_RENTAL_HISTORY H
ON H.CAR_ID = C.CAR_ID
AND H.START_DATE <= '2022-11-30'
AND H.END_DATE >= '2022-11-01'
WHERE C.CAR_TYPE IN ('세단', 'SUV')
AND H.HISTORY_ID IS NULL
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) >= 500000
AND (C.DAILY_FEE * 30 * (100 - P.DISCOUNT_RATE) / 100) < 2000000
ORDER BY FEE DESC, C.CAR_TYPE ASC, C.CAR_ID DESC;
5월 식품들의 총매출 조회하기
FOOD_PRODUCT와 FOOD_ORDER 테이블에서 생산일자가 2022년 5월인 식품들의 식품 ID, 식품 이름, 총매출을 조회하는 SQL문을 작성해주세요. 이때 결과는 총매출을 기준으로 내림차순 정렬해주시고 총매출이 같다면 식품 ID를 기준으로 오름차순 정렬해주세요.
-- Oracle
SELECT P.PRODUCT_ID, P.PRODUCT_NAME, SUM(P.PRICE * O.AMOUNT) AS TOTAL_SALES
FROM FOOD_PRODUCT P
JOIN FOOD_ORDER O
ON P.PRODUCT_ID = O.PRODUCT_ID
WHERE O.PRODUCE_DATE >= DATE '2022-05-01'
AND O.PRODUCE_DATE < DATE '2022-06-01'
GROUP BY P.PRODUCT_ID, P.PRODUCT_NAME
ORDER BY TOTAL_SALES DESC, P.PRODUCT_ID
-- MySQL
SELECT P.PRODUCT_ID, P.PRODUCT_NAME, SUM(P.PRICE * O.AMOUNT) AS TOTAL_SALES
FROM FOOD_PRODUCT P
JOIN FOOD_ORDER O
ON P.PRODUCT_ID = O.PRODUCT_ID
WHERE O.PRODUCE_DATE >= '2022-05-01'
AND O.PRODUCE_DATE < '2022-06-01'
GROUP BY P.PRODUCT_ID, P.PRODUCT_NAME
ORDER BY TOTAL_SALES DESC, P.PRODUCT_ID
단건 매출
P.PRICE * O.AMOUNT AS TOTAL_SALES
총 매출
SUM(P.PRICE * O.AMOUNT) AS TOTAL_SALES
조건에 맞는 도서와 저자 리스트 출력하기
'경제' 카테고리에 속하는 도서들의 도서 ID(BOOK_ID), 저자명(AUTHOR_NAME), 출판일(PUBLISHED_DATE) 리스트를 출력하는 SQL문을 작성해주세요.
결과는 출판일을 기준으로 오름차순 정렬해주세요.
-- MySQL
SELECT B.BOOK_ID, A.AUTHOR_NAME,
DATE_FORMAT(B.PUBLISHED_DATE, '%Y-%m-%d') AS PUBLISHED_DATE
FROM BOOK B
JOIN AUTHOR A
ON B.AUTHOR_ID = A.AUTHOR_ID
WHERE B.CATEGORY = '경제'
ORDER BY PUBLISHED_DATE
-- Oracle
SELECT B.BOOK_ID, A.AUTHOR_NAME, TO_CHAR(B.PUBLISHED_DATE, 'YYYY-MM-DD') AS PUBLISHED_DATE
FROM BOOK B
JOIN AUTHOR A
ON B.AUTHOR_ID = A.AUTHOR_ID
WHERE B.CATEGORY = '경제'
ORDER BY PUBLISHED_DATE
그룹별 조건에 맞는 식당 목록 출력하기
MEMBER_PROFILE와 REST_REVIEW 테이블에서 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회하는 SQL문을 작성해주세요. 회원 이름, 리뷰 텍스트, 리뷰 작성일이 출력되도록 작성해주시고, 결과는 리뷰 작성일을 기준으로 오름차순, 리뷰 작성일이 같다면 리뷰 텍스트를 기준으로 오름차순 정렬해주세요.
-- MySQL
SELECT P.MEMBER_NAME, R.REVIEW_TEXT,
DATE_FORMAT(R.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM MEMBER_PROFILE P
JOIN REST_REVIEW R
ON P.MEMBER_ID = R.MEMBER_ID
WHERE R.MEMBER_ID = (
SELECT MEMBER_ID
FROM REST_REVIEW
GROUP BY MEMBER_ID
ORDER BY COUNT(*) DESC
LIMIT 1
)
ORDER BY R.REVIEW_DATE, R.REVIEW_TEXT
-- Oraclel
TO_CHAR(r.REVIEW_DATE, 'YYYY-MM-DD') 날짜 포맷만 변경
-- MySQL
SELECT P.MEMBER_NAME, R.REVIEW_TEXT,
DATE_FORMAT(R.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM MEMBER_PROFILE P
JOIN REST_REVIEW R
ON P.MEMBER_ID = R.MEMBER_ID
WHERE P.MEMBER_ID IN (
SELECT X.MEMBER_ID
FROM (
SELECT
MEMBER_ID,
RANK() OVER(ORDER BY COUNT(*) DESC) AS RK
FROM REST_REVIEW
GROUP BY MEMBER_ID
) X
WHERE X.RK = 1
)
ORDER BY R.REVIEW_DATE ASC, R.REVIEW_TEXT ASC;
ORDER BY COUNT(*)
SELECT member_id
FROM rest_review
GROUP BY member_id
ORDER BY COUNT(*) DESC;
- FROM - WHERE -GROUP BY - HAVING (집계 존재) - SELECT - WINDOW - ORDER BY - LIMIT 순으로 동작하기 때문에 집계가 존재하고 난 후 부터는 사용이 가능하다.
- 물론 SELECT COUNT(*) FROM review; 같이 출력 결과가 한 개 일 때는 GROUP BY 없이 사용할 수 있다.
WINDOW FUNCTION
- SELECT 안에서만 정의 가능
- 다른 곳에서 필요하면 서브쿼리를 사용
함수 기본 구조
함수() OVER (
PARTITION BY 그룹
ORDER BY 정렬
)
- OVER(): window 함수 사용 선언
- PARTITION BY: 그룹 나누기 (GROUP BY랑 비슷하지만 행 유지)
- ORDER BY: 그룹 내부 정렬
NTILE(n): 상위 25%, 50% 같은 통계용
NTILE(4) OVER(ORDER BY score DESC)
ROW_NUMBER(): 동일 값에도 다른 순번을 매김.
RANK(): 공동 순위는 같은 값을 매기고 그 수만큼 점프.
DENSE_RANK(): 공동 순위는 같은 값을 매기지만 점프 없음.
SUM() OVER: 누적합/누적 그룹
COUNT() OVER: 개수/페이징
LAG(): 이전 값 가져오기 (ORDER BY 기준)
전날 대비 증가량
SELECT DATE, AMOUNT,
AMOUNT - LAG(AMOUNT) OVER(ORDER BY DATE) AS DIFF
FROM SALES;
전날 매출 비교
SELECT DATE, AMOUNT,
LAG(AMOUNT) OVER(ORDER BY DATE) AS PREV_AMOUNT
FROM SALES;
LEAD(): 다음 값 가져오기 (ORDER BY 기준)
WINDOW vs GROUP BY
GROUP BY는 사용하면 행이 조건에 따라 줄어들지만 WINDOW 함수는 줄어들지 않음.
없어진 기록 찾기
천재지변으로 인해 일부 데이터가 유실되었습니다. 입양을 간 기록은 있는데, 보호소에 들어온 기록이 없는 동물의 ID와 이름을 ID 순으로 조회하는 SQL문을 작성해주세요.
SELECT o.animal_id, o.name
FROM animal_outs o
LEFT JOIN animal_ins i
ON o.animal_id = i.animal_id
WHERE i.animal_id IS NULL
ORDER BY o.animal_id
- 가독성과 새끼손가락 건강을 위해서 키워드는 대문자로 쓰고 데이터 관련은 소문자로 표시하는게 좋을 거 같다.
있었는데요 없었습니다
관리자의 실수로 일부 동물의 입양일이 잘못 입력되었습니다. 보호 시작일보다 입양일이 더 빠른 동물의 아이디와 이름을 조회하는 SQL문을 작성해주세요. 이때 결과는 보호 시작일이 빠른 순으로 조회해야합니다.
SELECT i.animal_id, o.name
FROM animal_ins i
JOIN animal_outs o
ON i.animal_id = o.animal_id
WHERE i.datetime > o.datetime
ORDER BY i.datetime
오랜 기간 보호한 동물(1)
아직 입양을 못 간 동물 중, 가장 오래 보호소에 있었던 동물 3마리의 이름과 보호 시작일을 조회하는 SQL문을 작성해주세요. 이때 결과는 보호 시작일 순으로 조회해야 합니다.
LEFT JOIN
SELECT i.name, i.datetime
FROM animal_ins i
LEFT JOIN animal_outs o
ON i.animal_id = o.animal_id
WHERE o.animal_id IS NULL
ORDER BY i.datetime ASC
LIMIT 3
- LEFT JOIN 후 WHERE에 오른쪽 테이블 조건 쓰면 → INNER JOIN 된다.
- Oracle의 경우, 'LIMIT 3' 대신 'FETCH FIRST 3 ROWS ONLY'
NOT EXISTS
SELECT name, datetime
FROM animal_ins i
WHERE NOT EXISTS (
SELECT 1
FROM animal_outs o
WHERE o.animal_id = i.animal_id
)
ORDER BY datetime
LIMIT 3;
NOT IN (중간에 하나만 NULL 값이 있어도 전체가 실패하기 때문에 실무에서는 사용하지 않음,.)
SELECT
NAME, DATETIME
FROM ANIMAL_INS
WHERE ANIMAL_ID NOT IN (SELECT ANIMAL_ID FROM ANIMAL_OUTS)
ORDER BY DATETIME ASC LIMIT 3
- SQL에서 UNKNOWN 포함되면 전체 조건 FALSE 처리됨.
WHERE animal_id NOT IN (A, B, NULL)
- 이라는 쿼리를 짰을 때,
- 'id != A AND id != B AND id != NULL' 라는 DB 내부의 로직이 실행되는데, id != NULL → 항상 UNKNOWN이면 전체 조건 FALSE 처리됨.
보호소에서 중성화한 동물
보호소에서 중성화 수술을 거친 동물 정보를 알아보려 합니다. 보호소에 들어올 당시에는 중성화되지 않았지만, 보호소를 나갈 당시에는 중성화된 동물의 아이디와 생물 종, 이름을 조회하는 아이디 순으로 조회하는 SQL 문을 작성해주세요.
SELECT i.animal_id, i.animal_type, i.name
FROM animal_ins i
JOIN animal_outs o
ON i.animal_id = o.animal_id
AND (
o.sex_upon_outcome LIKE 'Spayed%'
OR o.sex_upon_outcome LIKE 'Neutered%'
)
WHERE i.sex_upon_intake LIKE 'Intact%'
ORDER BY i.animal_id
상품 별 오프라인 매출 구하기
PRODUCT 테이블과 OFFLINE_SALE 테이블에서 상품코드 별 매출액(판매가 * 판매량) 합계를 출력하는 SQL문을 작성해주세요. 결과는 매출액을 기준으로 내림차순 정렬해주시고 매출액이 같다면 상품코드를 기준으로 오름차순 정렬해주세요.
SELECT p.product_code,
SUM(p.price * s.sales_amount) AS total_sale
FROM product p
JOIN offline_sale s
ON p.product_id = s.product_id
GROUP BY p.product_code
ORDER BY total_sale DESC, p.product_code
상품을 구매한 회원 비율 구하기
USER_INFO 테이블과 ONLINE_SALE 테이블에서 2021년에 가입한 전체 회원들 중 상품을 구매한 회원수와 상품을 구매한 회원의 비율(=2021년에 가입한 회원 중 상품을 구매한 회원수 / 2021년에 가입한 전체 회원 수)을 년, 월 별로 출력하는 SQL문을 작성해주세요. 상품을 구매한 회원의 비율은 소수점 두번째자리에서 반올림하고, 전체 결과는 년을 기준으로 오름차순 정렬해주시고 년이 같다면 월을 기준으로 오름차순 정렬해주세요.
WITH user_cnt AS (
SELECT COUNT(*) AS cnt
FROM user_info
WHERE joined >= DATE '2021-01-01'
AND joined < DATE '2022-01-01'
)
SELECT
EXTRACT(YEAR FROM s.sales_date) AS YEAR,
EXTRACT(MONTH FROM s.sales_date) AS MONTH,
COUNT(DISTINCT i.user_id) AS PURCHASED_USERS,
ROUND(COUNT(DISTINCT i.user_id) * 1.0 / (SELECT cnt FROM user_cnt), 1) AS PUCHASED_RATIO
FROM user_info i
JOIN online_sale s
ON i.user_id = s.user_id
WHERE i.joined >= DATE '2021-01-01'
AND i.joined < DATE '2022-01-01'
GROUP BY
EXTRACT(YEAR FROM s.sales_date),
EXTRACT(MONTH FROM s.sales_date)
ORDER BY YEAR, MONTH;
FrontEnd 개발자 찾기
DEVELOPERS 테이블에서 Front End 스킬을 가진 개발자의 정보를 조회하려 합니다. 조건에 맞는 개발자의 ID, 이메일, 이름, 성을 조회하는 SQL 문을 작성해 주세요. 결과는 ID를 기준으로 오름차순 정렬해 주세요.
SELECT DISTINCT d.id, d.email, d.first_name, d.last_name
FROM developers d
JOIN skillcodes s
ON d.skill_code & s.code <> 0
AND s.category = 'Front End'
ORDER BY d.id
'Database > SQL' 카테고리의 다른 글
| String, Date (0) | 2026.02.09 |
|---|---|
| GROUP BY (0) | 2026.02.09 |
| SUM, MAX, MIN (0) | 2026.02.07 |
| IS NULL (0) | 2026.02.07 |
| SELECT (0) | 2026.02.07 |