본문 바로가기
Database/SQL

GROUP BY

by curious week 2026. 2. 9.

자동차 대여 기록에서 대여중 / 대여 가능 여부 구분하기(존재 여부 CASE-MAX, 전부 만족 CASE-MIN, 개수 CASE-COUNT)

CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 2022년 10월 16일에 대여 중인 자동차인 경우 '대여중' 이라고 표시하고, 대여 중이지 않은 자동차인 경우 '대여 가능'을 표시하는 컬럼(컬럼명: AVAILABILITY)을 추가하여 자동차 ID와 AVAILABILITY 리스트를 출력하는 SQL문을 작성해주세요. 이때 반납 날짜가 2022년 10월 16일인 경우에도 '대여중'으로 표시해주시고 결과는 자동차 ID를 기준으로 내림차순 정렬해주세요.

MAX

SELECT
    car_id,
    CASE
        WHEN MAX(
            CASE
                WHEN start_date <= '2022-10-16'
                    AND end_date >= '2022-10-16'
                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
  • MAX를 쓰는 이유 기간에 포함되면 1로 아니면 0으로 두고 기간에 포함되는 큰 값(1)을 받으려고 쓴다.

EXISTS

SELECT
    car_id,
    CASE
        WHEN EXISTS (
            SELECT 1
            FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY h2
            WHERE h2.car_id = h1.car_id
              AND start_date <= '2022-10-16'
              AND end_date >= '2022-10-16'
        )
        THEN '대여중'
        ELSE '대여 가능'
    END AS availability
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY h1
GROUP BY car_id
ORDER BY car_id DESC;

GROUP BY에서 사용하는 MAX, MIN, COUNT

MAX(존재 여부)

SELECT user_id
FROM task
GROUP BY user_id
HAVING MAX(
    CASE 
        WHEN done='완료' THEN 1
        ELSE 0
    END
) = 1;

MIN (전부 만족)

SELECT user_id
FROM task
GROUP BY user_id
HAVING MIN(
    CASE 
        WHEN done = '완료' THEN 1
        ELSE 0
    END
) = 1;
  • 그룹 전체가 완료면 min=1 아니면 0

COUNT(개수)

SELECT user_id
FROM task
GROUP BY user_id
HAVING COUNT(*) = 
       COUNT(CASE WHEN done='완료' THEN 1 END);

WHERE vs HAVING 차이

WHERE = 행 필터

SELECT *
FROM exam
WHERE score >= 80;

 

  • 그룹이 생기기 전이기 때문에 집계함수를 사용하지 못함.

HAVING = 그룹 필터

SELECT student, AVG(score)
FROM exam
GROUP BY student
HAVING AVG(score) >= 80;
  • 그룹이 이뤄졌기 때문에 집계함수(SUM, AVG, COUNT, MAX, MIN)를 사용 가능하다.

저자 별 카테고리 별 매출액 집계하기 (GROUP BY)

2022년 1월의 도서 판매 데이터를 기준으로 저자 별, 카테고리 별 매출액(TOTAL_SALES = 판매량 * 판매가) 을 구하여, 저자 ID(AUTHOR_ID), 저자명(AUTHOR_NAME), 카테고리(CATEGORY), 매출액(SALES) 리스트를 출력하는 SQL문을 작성해주세요.
결과는 저자 ID를 오름차순으로, 저자 ID가 같다면 카테고리를 내림차순 정렬해주세요.

SELECT a.author_id, a.author_name, b.category, 
       SUM(b.price * s.sales) AS TOTAL_SALES
FROM book b
JOIN author a
  ON b.author_id = a.author_id
JOIN book_sales s
  ON b.book_id = s.book_id
WHERE s.sales_date >= DATE '2022-01-01'
  AND s.sales_date < DATE '2022-02-01'
GROUP BY a.author_id, a.author_name, b.category
ORDER BY a.author_id, b.category DESC

GROUP BY 내부 그룹화 조금 더 심도 있게 이해하기

테이블

author_id | category
--------------------
1         IT
1         IT
1         Novel
2         IT
2         IT
2         Novel

쿼리

GROUP BY author_id, category

조합 종류

(1, IT)     → 2행
(1, Novel)  → 1행
(2, IT)     → 2행
(2, Novel)  → 1행

성분으로 구분한 아이스크림 총 주문량

상반기 동안 각 아이스크림 성분 타입과 성분 타입에 대한 아이스크림의 총주문량을 총주문량이 작은 순서대로 조회하는 SQL 문을 작성해주세요. 이때 총주문량을 나타내는 컬럼명은 TOTAL_ORDER로 지정해주세요.

SELECT i.ingredient_type, SUM(f.total_order) AS TOTAL_ORDER
FROM first_half f
JOIN icecream_info i
  ON f.flavor = i.flavor
GROUP BY i.ingredient_type
ORDER BY TOTAL_ORDER

ORDER BY는 “SELECT 결과에서 의미가 확정된 값”만 사용할 수 있다.

ORDER BY f.ingredient_type -- 불가능

-- 가능한 쿼리 --
ORDER BY TOTAL_ORDER -- 또는
ORDER BY SUM(f.total_order)

식품분류별 가장 비싼 식품의 정보 조회하기

FOOD_PRODUCT 테이블에서 식품분류별로 가격이 제일 비싼 식품의 분류, 가격, 이름을 조회하는 SQL문을 작성해주세요. 이때 식품분류가 '과자', '국', '김치', '식용유'인 경우만 출력시켜 주시고 결과는 식품 가격을 기준으로 내림차순 정렬해주세요.

WITH classification AS (
    SELECT category, MAX(price) OVER(PARTITION BY category) AS MAX_VAL
    FROM FOOD_PRODUCT
    WHERE category IN ('과자', '국', '김치', '식용유')
    GROUP BY category, price
    ORDER BY price DESC
)

SELECT f.category, f.price ,f.product_name DISTINCT
FROM food_product f
JOIN classification c
  ON c.max_val = f.price
WHERE f.category IN ('과자', '국', '김치', '식용유')
GROUP BY f.product_name, f.category, f.price
ORDER BY f.price DESC
  • 통과는 하지만 아쉬운 지점이 있다. classification의 인스턴스 값이 중복 출력된다.
  • 이때는 윈도우 함수 말고 아래처럼 집계 함수를 사용하는게 좋다.
WITH max_price AS (
    SELECT category, MAX(price) AS max_price
    FROM FOOD_PRODUCT
    WHERE category IN ('과자', '국', '김치', '식용유')
    GROUP BY category
)

SELECT f.category, f.price ,f.product_name
FROM food_product f
JOIN max_price m
  ON m.category = f.category
  AND m.max_price = f.price
GROUP BY f.product_name, f.category, f.price
ORDER BY f.price DESC

진료과별 총 예약 횟수 출력하기

APPOINTMENT 테이블에서 2022년 5월에 예약한 환자 수를 진료과코드 별로 조회하는 SQL문을 작성해주세요. 이때, 컬럼명은 '진료과 코드', '5월예약건수'로 지정해주시고 결과는 진료과별 예약한 환자 수를 기준으로 오름차순 정렬하고, 예약한 환자 수가 같다면 진료과 코드를 기준으로 오름차순 정렬해주세요.

  • 예약환자 수를 찾는 문제라서 WHERE 조건에 (apnt_cncl_yn IS NULL OR UPPER(apnt_cncl_yn) = 'N') 이런 쿼리를 넣었었는데 계속 실패했다. 아래처럼 그냥 기간만 조회하면 된다.
SELECT mcdp_cd AS "진료과 코드", COUNT(*) AS "5월예약건수"
FROM appointment
WHERE apnt_ymd >= DATE '2022-05-01'
  AND apnt_ymd < DATE '2022-06-01'
GROUP BY mcdp_cd
ORDER BY 2, 1

즐겨찾기가 가장 많은 식당 정보 출력하기

REST_INFO 테이블에서 음식종류별로 즐겨찾기수가 가장 많은 식당의 음식 종류, ID, 식당 이름, 즐겨찾기수를 조회하는 SQL문을 작성해주세요. 이때 결과는 음식 종류를 기준으로 내림차순 정렬해주세요.

SELECT r.food_type, r.rest_id, r.rest_name, r.favorites
FROM rest_info r
JOIN (
    SELECT food_type, MAX(favorites) AS max_fav
    FROM rest_info
    GROUP BY food_type
) i ON r.food_type = i.food_type
    AND r.favorites = i.max_fav
GROUP BY r.food_type, r.rest_id, r.rest_name, r.favorites
ORDER BY r.food_type DESC

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

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

  • '특정 월의 총 대여 횟수가 0인 경우에는 결과에서 제외' 이 부분을 보고 모든 월에 데이터가 있는지 체크해서 틀렸다. 특정 월의 총 대여 횟수가 0인 경우에는 결과에서 제외라는 것은 출력하지 말라는 의미다.
WITH base AS (
    SELECT car_id, start_date
    FROM car_rental_company_rental_history
    WHERE start_date >= DATE '2022-08-01'
      AND start_date < DATE '2022-11-01' 
), part AS (
    SELECT car_id
    FROM base
    GROUP BY car_id
    HAVING COUNT(*) >= 5
)
SELECT DISTINCT EXTRACT(MONTH FROM b.start_date) AS month, b.car_id, COUNT(*) AS records
FROM base b
JOIN part p
  ON p.car_id = b.car_id
GROUP BY b.car_id, EXTRACT(MONTH FROM b.start_date)
ORDER BY month, b.car_id DESC
WITH base AS (
    SELECT car_id, start_date
    FROM car_rental_company_rental_history
    WHERE start_date >= DATE '2022-08-01'
      AND start_date < DATE '2022-11-01' 
)

SELECT DISTINCT EXTRACT(MONTH FROM start_date) AS month, 
       car_id, COUNT(*) AS records
FROM base
WHERE car_id IN (
    SELECT car_id
    FROM base
    GROUP BY car_id
    HAVING COUNT(*) >= 5
)
GROUP BY car_id, EXTRACT(MONTH FROM start_date)
ORDER BY month, car_id DESCa
WITH base AS (
    SELECT car_id, start_date,
        COUNT(*) OVER (PARTITION BY car_id) AS cnt
    FROM car_rental_company_rental_history
    WHERE start_date >= DATE '2022-08-01'
      AND start_date < DATE '2022-11-01' 
)

SELECT DISTINCT EXTRACT(MONTH FROM start_date) AS month, 
       car_id, COUNT(*) AS records
FROM base
WHERE cnt >= 5
GROUP BY car_id, EXTRACT(MONTH FROM start_date)
ORDER BY month, car_id DESC

자동차 종류 별 특정 옵션이 포함된 자동차 수 구하기

CAR_RENTAL_COMPANY_CAR 테이블에서 '통풍시트', '열선시트', '가죽시트' 중 하나 이상의 옵션이 포함된 자동차가 자동차 종류 별로 몇 대인지 출력하는 SQL문을 작성해주세요. 이때 자동차 수에 대한 컬럼명은 CARS로 지정하고, 결과는 자동차 종류를 기준으로 오름차순 정렬해주세요.

SELECT car_type, COUNT(*) AS cars
FROM car_rental_company_car
WHERE INSTR(options, '통풍시트') > 0
  OR INSTR(options,'열선시트') > 0
  OR INSTR(options,'가죽시트') > 0
GROUP BY car_type
ORDER BY car_type

카테고리 별 도서 판매량 집계하기

2022년 1월의 카테고리 별 도서 판매량을 합산하고, 카테고리(CATEGORY), 총 판매량(TOTAL_SALES) 리스트를 출력하는 SQL문을 작성해주세요. 결과는 카테고리명을 기준으로 오름차순 정렬해주세요.

SELECT b.category, SUM(s.sales) AS total_sales
FROM book b
JOIN book_sales s
  ON b.book_id = s.book_id
WHERE s.sales_date >= DATE '2022-01-01'
  AND s.sales_date < DATE '2022-02-01'
GROUP BY b.category
ORDER BY b.category

조건에 맞는 사용자와 총 거래금액 조회하기

USED_GOODS_BOARD와 USED_GOODS_USER 테이블에서 완료된 중고 거래의 총금액이 70만 원 이상인 사람의 회원 ID, 닉네임, 총거래금액을 조회하는 SQL문을 작성해주세요. 결과는 총거래금액을 기준으로 오름차순 정렬해주세요.

SELECT u.user_id, u.nickname, SUM(b.price) AS total_sales
FROM used_goods_board b
JOIN used_goods_user u
  ON b.writer_id = u.user_id
WHERE b.status = 'DONE'
GROUP BY u.user_id, u.nickname
HAVING SUM(b.price) >= 700000
ORDER BY total_sales

고양이와 개는 몇 마리 있을까

동물 보호소에 들어온 동물 중 고양이와 개가 각각 몇 마리인지 조회하는 SQL문을 작성해주세요. 이때 고양이를 개보다 먼저 조회해주세요.

SELECT animal_type, COUNT(*) AS count
FROM animal_ins
WHERE UPPER(animal_type) = 'DOG' OR UPPER(animal_type) = 'CAT'
GROUP BY animal_type
ORDER BY animal_type

동명 동물 수 찾기

동물 보호소에 들어온 동물 이름 중 두 번 이상 쓰인 이름과 해당 이름이 쓰인 횟수를 조회하는 SQL문을 작성해주세요. 이때 결과는 이름이 없는 동물은 집계에서 제외하며, 결과는 이름 순으로 조회해주세요.

SELECT name, COUNT(*) AS count
FROM animal_ins
WHERE name IS NOT NULL
GROUP BY name
HAVING COUNT(*) >= 2
ORDER BY name

년, 월, 성별 별 상품 구매 회원 수 구하기

USER_INFO 테이블과 ONLINE_SALE 테이블에서 년, 월, 성별 별로 상품을 구매한 회원수를 집계하는 SQL문을 작성해주세요. 결과는 년, 월, 성별을 기준으로 오름차순 정렬해주세요. 이때, 성별 정보가 없는 경우 결과에서 제외해주세요.

  • SELECT절 COUNT(*)는 구매건수라서 COUNT를 user_id를 중복없이 출력해야 회원수를 집계 할 수 있다.
SELECT EXTRACT(YEAR FROM s.sales_date) AS year, 
	   EXTRACT(MONTH FROM s.sales_date) AS month, 
       i.gender, 
       COUNT(DISTINCT s.user_id) AS users
FROM user_info i
JOIN online_sale s
  ON i.user_id = s.user_id
  AND i.gender IS NOT NULL
GROUP BY EXTRACT(YEAR FROM s.sales_date), EXTRACT(MONTH FROM s.sales_date), i.gender
ORDER BY year, month, i.gender

입양 시각 구하기(1) (CAST)

보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다. 09:00부터 19:59까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요. 이때 결과는 시간대 순으로 정렬해야 합니다.

-- MySQL
SELECT EXTRACT(HOUR FROM datetime), COUNT(*)
FROM animal_outs
WHERE EXTRACT(HOUR FROM datetime) >= 9
  AND EXTRACT(HOUR FROM datetime) < 20
GROUP BY EXTRACT(HOUR FROM datetime)
ORDER BY EXTRACT(HOUR FROM datetime)
-- Oracle
SELECT EXTRACT(HOUR FROM CAST(datetime AS timestamp)) AS hour, COUNT(*) AS count
FROM animal_outs
WHERE EXTRACT(HOUR FROM CAST(datetime AS timestamp)) >= 9
  AND EXTRACT(HOUR FROM CAST(datetime AS timestamp)) < 20
GROUP BY EXTRACT(HOUR FROM CAST(datetime AS timestamp))
ORDER BY hour

CAST

CAST(값 AS 타입)
  • MySQL 타입: INT, CHAR, DATE, DATETIME
  • Oracle 타입: NUMBER, VARCHAR2, DATE, TIMESTAMP
CAST('123' AS INT) -- 문자열 → 숫자
CAST('123' AS INTEGER)

CAST(123 AS VARCHAR(10)) -- 숫자 → 문자열
CAST(123 AS VARCHAR) 

CAST('2024-01-01' AS DATE) -- 문자열 → 날짜
TO_DATE('2024-01-01','YYYY-MM-DD') -- Oracle에서는 TO_DATE를 많이 사용

CAST(date_col AS VARCHAR) -- 날짜 → 문자열
TO_CHAR(date_col,'YYYY-MM-DD')

CAST(date_col AS TIMESTAMP) -- DATE → TIMESTAMP
타입 자체 변환 CAST
날짜 포맷 변환 TO_CHAR
문자열 → 날짜 파싱 TO_DATE

시간 추출이 안될 때, 아래처럼 사용할 수 있다.

EXTRACT(HOUR FROM CAST(col AS TIMESTAMP))

입양 시각 구하기(2)

보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다. 0시부터 23시까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요. 이때 결과는 시간대 순으로 정렬해야 합니다.

-- MySQL
WITH RECURSIVE hours AS (
    SELECT 0 AS hour
    UNION ALL
    SELECT hour + 1 FROM hours WHERE hour < 23
)
SELECT h.hour ,COUNT(o.datetime) AS count
FROM hours h
LEFT JOIN animal_outs o
    ON EXTRACT(HOUR FROM o.datetime) = h.hour
GROUP BY h.hour
ORDER BY h.hour
-- Oracle
WITH hours AS (
    SELECT LEVEL-1 AS hour
    FROM dual
    CONNECT BY LEVEL <= 24
)
SELECT h.hour, COUNT(o.datetime) AS count
FROM hours h
LEFT JOIN animal_outs o
    ON EXTRACT(HOUR FROM CAST(o.datetime AS timestamp)) = h.hour
GROUP BY h.hour
ORDER BY h.hour

DUAL - Oracle에서 제공하는 1행짜리 더미 데이터

SELECT * FROM dual;

# 아래 결과가 출력된다.

dummy
------
  X

SELECT sysdate as now FROM dual

# 아래 결과가 출력된다.

now
-------
2026-02-08 18:10:58
select level - 1 as hour
from dual
connect by level <= 24;

 

  • dual은 1행이다.
  • connect by는 그 1행을 “자식”으로 계속 확장해 여러 행을 만든다. 
  • 확장될 때마다 Oracle이 제공하는 의사 컬럼 LEVEL이 1,2,3... 증가한다
  • CONNECT BY = “행을 반복 생성하는 Oracle 전용 문법”, 재귀 반복 수라고 할 수 있다.
  • LEVEL은 '반복 수'를 뜻한다.

RECURSIVE - MySQL에서 재귀로 더미데이터 생성

WITH RECURSIVE hours AS (
    SELECT 0 as hour              -- (1) 앵커(시작 행)
    UNION ALL
    SELECT hour + 1               -- (2) 재귀(다음 행 생성)
    FROM hours
    WHERE hour < 23               -- (3) 종료 조건(여기 없으면 무한 루프)
)
SELECT hour FROM hours;

 

WITH RECURSIVE 
max_price AS (
    SELECT MAX(price) AS max_price
    FROM product
),
 price_group AS (
    SELECT 0 AS price
    UNION ALL
    SELECT pg.price + 10000
    FROM price_group pg
    WHERE pg.price <= (SELECT max_price FROM max_price)
)
SELECT price
FROM price_group
  • 가격 구간 더미 생성

가격대 별 상품 개수 구하기(TRUNCATE, TRUNC)

PRODUCT 테이블에서 만원 단위의 가격대 별로 상품 개수를 출력하는 SQL 문을 작성해주세요. 이때 컬럼명은 각각 컬럼명은 PRICE_GROUP, PRODUCTS로 지정해주시고 가격대 정보는 각 구간의 최소금액(10,000원 이상 ~ 20,000 미만인 구간인 경우 10,000)으로 표시해주세요. 결과는 가격대를 기준으로 오름차순 정렬해주세요.

-- MySQL
SELECT TRUNCATE(price, -4) AS price_group, COUNT(*) AS products 
FROM product
GROUP BY TRUNCATE(price, -4)
ORDER BY price_group
-- Oracle
SELECT TRUNC(price, -4) AS price_group, COUNT(*) AS products
FROM product
GROUP BY TRUNC(price, -4)
ORDER BY price_group

TRUNCATE (버림)

  • Oracle: TRUNC(n, 자리수)
  • MySQL: TRUNCATE(n, 자리수)
TRUNC(price, -4)
-- 동일한 효과
FLOOR(price / 10000) * 10000

Oracle의 TRUNC(date)는 의미가 또 다르다.

  • TRUNC(some_date) → 시간 버리고 날짜만 남김 (00:00:00)
  • TRUNC(some_date, 'MM') → 그 달 1일로 맞춤
  • TRUNC(some_date, 'YYYY') → 그 해 1월 1일로 맞춤

언어별 개발자 분류하기

DEVELOPERS 테이블에서 GRADE별 개발자의 정보를 조회하려 합니다. GRADE는 다음과 같이 정해집니다.
A : Front End 스킬과 Python 스킬을 함께 가지고 있는 개발자
B : C# 스킬을 가진 개발자
C : 그 외의 Front End 개발자
GRADE가 존재하는 개발자의 GRADE, ID, EMAIL을 조회하는 SQL 문을 작성해 주세요. 결과는 GRADE와 ID를 기준으로 오름차순 정렬해 주세요.

CTE

WITH frontend AS (
    SELECT SUM(code) AS fe FROM skillcodes WHERE UPPER(category) = 'FRONT END'
),
python AS (
    SELECT CODE AS py FROM skillcodes WHERE UPPER(name) = 'PYTHON'
),
csharp AS (
    SELECT CODE AS cs FROM skillcodes WHERE UPPER(name) = 'C#'
)

SELECT 
    CASE
        WHEN skill_code & fe <> 0 AND skill_code & py <> 0 THEN 'A'
        WHEN skill_code & cs <> 0 THEN 'B'
        WHEN skill_code & fe <> 0 THEN 'C'
    END AS grade, id, email
FROM developers
JOIN frontend JOIN python JOIN csharp
HAVING grade IS NOT NULL
ORDER BY grade, id

CROSS JOIN

WITH codes AS (
  SELECT
    (SELECT SUM(code) FROM skillcodes WHERE UPPER(category) = 'FRONT END') AS fc,
    (SELECT code      FROM skillcodes WHERE UPPER(name) = 'PYTHON')        AS py,
    (SELECT code      FROM skillcodes WHERE UPPER(name) = 'C#')            AS cs
)

SELECT
  CASE
    WHEN (d.skill_code & c.fc) <> 0 AND (d.skill_code & c.py) <> 0 THEN 'A'
    WHEN (d.skill_code & c.cs) <> 0                                THEN 'B'
    WHEN (d.skill_code & c.fc) <> 0                                THEN 'C'
  END AS grade, d.id, d.email
FROM developers d
CROSS JOIN codes c
WHERE
  ((d.skill_code & c.fc) <> 0 AND (d.skill_code & c.py) <> 0)
  OR ((d.skill_code & c.cs) <> 0)
  OR ((d.skill_code & c.fc) <> 0)
ORDER BY grade, d.id;
  • Oracle의 경우는 위 방식대로 사용할 수 없으므로 dual(더미)을 이용해야한다.
-- Oracle
with codes as (
  select
    (select sum(code) from skillcodes
     where upper(category) = 'FRONT END') as fc,
    (select code from skillcodes
     where upper(name) = 'PYTHON') as py,
    (select code from skillcodes
     where upper(name) = 'C#') as cs
  from dual
),
graded as (
  select
    case
      when (d.skill_code & c.fc) <> 0 and (d.skill_code & c.py) <> 0 then 'A'
      when (d.skill_code & c.cs) <> 0                                then 'B'
      when (d.skill_code & c.fc) <> 0                                then 'C'
    end as grade, d.id, d.email
  from developers d
  cross join codes c
)

select grade, id, email
from graded
where grade is not null
order by grade, id;

조건에 맞는 사원 정보 조회하기

HR_DEPARTMENT, HR_EMPLOYEES, HR_GRADE 테이블에서 2022년도 한해 평가 점수가 가장 높은 사원 정보를 조회하려 합니다. 2022년도 평가 점수가 가장 높은 사원들의 점수, 사번, 성명, 직책, 이메일을 조회하는 SQL문을 작성해주세요.
2022년도의 평가 점수는 상,하반기 점수의 합을 의미하고, 평가 점수를 나타내는 컬럼의 이름은 SCORE로 해주세요.

SELECT SUM(g.score) AS score, e.emp_no, e.emp_name, e.position, e.email
FROM hr_department d
JOIN hr_employees e
  ON d.dept_id = e.dept_id
JOIN hr_grade g
  ON e.emp_no = g.emp_no
GROUP BY e.emp_no
ORDER BY score DESC
LIMIT 1

연간 평가점수에 해당하는 평가 등급 및 성과금 조회하기

HR_DEPARTMENT, HR_EMPLOYEES, HR_GRADE 테이블을 이용해 사원별 성과금 정보를 조회하려합니다. 평가 점수별 등급과 등급에 따른 성과금 정보가 아래와 같을 때, 사번, 성명, 평가 등급, 성과금을 조회하는 SQL문을 작성해주세요.
평가등급의 컬럼명은 GRADE로, 성과금의 컬럼명은 BONUS로 해주세요. 결과는 사번 기준으로 오름차순 정렬해주세요.
기준 점수 평가 등급 성과금(연봉 기준): 96 이상 = S = 20%, 90 이상 = A = 15%, 80 이상 = B = 10%, 이외 = C = 0%

WITH bonus_grade AS (
    SELECT (
        CASE
          WHEN AVG(score) >= 96 THEN 'S'
          WHEN AVG(score) >= 90 THEN 'A'
          WHEN AVG(score) >= 80 THEN 'B'
          ELSE 'C'
        END
    ) AS grade, (
        CASE
          WHEN AVG(score) >= 96 THEN 0.2
          WHEN AVG(score) >= 90 THEN 0.15
          WHEN AVG(score) >= 80 THEN 0.1
          ELSE 0
        END
    ) AS grade_bn,
    emp_no
    FROM hr_grade
    WHERE year = 2022
    GROUP BY emp_no
)

SELECT e.emp_no, e.emp_name, b.grade, e.sal * b.grade_bn AS bonus
FROM hr_employees e
JOIN bonus_grade b
  ON b.emp_no = e.emp_no
GROUP BY e.emp_no, b.grade, b.grade_bn
ORDER BY e.emp_no

부서별 평균 연봉 조회하기

HR_DEPARTMENT와 HR_EMPLOYEES 테이블을 이용해 부서별 평균 연봉을 조회하려 합니다. 부서별로 부서 ID, 영문 부서명, 평균 연봉을 조회하는 SQL문을 작성해주세요. 평균연봉은 소수점 첫째 자리에서 반올림하고 컬럼명은 AVG_SAL로 해주세요. 결과는 부서별 평균 연봉을 기준으로 내림차순 정렬해주세요.

SELECT e.dept_id, d.dept_name_en, ROUND(AVG(e.sal), 0) AS avg_sal
FROM hr_employees e
JOIN hr_department d
  ON e.dept_id = d.dept_id
GROUP BY e.dept_id, d.dept_name_en
ORDER BY avg_sal DESC

노선별 평균 역 사이 거리 조회하기

SUBWAY_DISTANCE 테이블에서 노선별로 노선, 총 누계 거리, 평균 역 사이 거리를 노선별로 조회하는 SQL문을 작성해주세요.
총 누계거리는 테이블 내 존재하는 역들의 역 사이 거리의 총 합을 뜻합니다. 총 누계 거리와 평균 역 사이 거리의 컬럼명은 각각 TOTAL_DISTANCE, AVERAGE_DISTANCE로 해주시고, 총 누계거리는 소수 둘째자리에서, 평균 역 사이 거리는 소수 셋째 자리에서 반올림 한 뒤 단위(km)를 함께 출력해주세요.
결과는 총 누계 거리를 기준으로 내림차순 정렬해주세요.

SELECT ROUTE, 
       CONCAT(ROUND(SUM(d_between_dist), 1), 'km') AS TOTAL_DISTANCE, 
       CONCAT(ROUND(AVG(d_between_dist), 2), 'km') AS AVERAGE_DISTANCE
FROM SUBWAY_DISTANCE
GROUP BY ROUTE
ORDER BY ROUND(SUM(d_between_dist), 1) DESC
  • 이 문제에서 조심해야할 점은 ORDER BY에 total_distance를 넣으면 'km'를 넣은 문자값이기 때문에 오류가 발생할 수 있다.

물고기 종류 별 잡은 수 구하기

FISH_NAME_INFO에서 물고기의 종류 별 물고기의 이름과 잡은 수를 출력하는 SQL문을 작성해주세요. 물고기의 이름 컬럼명은 FISH_NAME, 잡은 수 컬럼명은 FISH_COUNT로 해주세요. 결과는 잡은 수 기준으로 내림차순 정렬해주세요.

SELECT COUNT(*) AS FISH_COUNT, n.fish_name AS FISH_NAME
FROM fish_info i
JOIN fish_name_info n
  ON n.fish_type = i.fish_type
GROUP BY i.fish_type, n.fish_name
ORDER BY FISH_COUNT DESC

월별 잡은 물고기 수 구하기

월별 잡은 물고기의 수와 월을 출력하는 SQL문을 작성해주세요. 잡은 물고기 수 컬럼명은 FISH_COUNT, 월 컬럼명은 MONTH로 해주세요. 결과는 월을 기준으로 오름차순 정렬해주세요.
단, 월은 숫자형태 (1~12) 로 출력하며 9 이하의 숫자는 두 자리로 출력하지 않습니다. 잡은 물고기가 없는 월은 출력하지 않습니다.

SELECT COUNT(*) AS FISH_COUNT, EXTRACT(MONTH FROM time) AS MONTH
FROM fish_info
GROUP BY EXTRACT(MONTH FROM time)
ORDER BY MONTH

특정 조건을 만족하는 물고기별 수와 최대 길이 구하기

FISH_INFO에서 평균 길이가 33cm 이상인 물고기들을 종류별로 분류하여 잡은 수, 최대 길이, 물고기의 종류를 출력하는 SQL문을 작성해주세요. 결과는 물고기 종류에 대해 오름차순으로 정렬해주시고, 10cm이하의 물고기들은 10cm로 취급하여 평균 길이를 구해주세요.
컬럼명은 물고기의 종류 'FISH_TYPE', 잡은 수 'FISH_COUNT', 최대 길이 'MAX_LENGTH'로 해주세요.

SELECT COUNT(*) AS FISH_COUNT, MAX(LENGTH) AS MAX_LENGTH, FISH_TYPE
FROM FISH_INFO
GROUP BY FISH_TYPE
HAVING AVG(COALESCE(LENGTH, 10)) >= 33
ORDER BY FISH_TYPE

 

'Database > SQL' 카테고리의 다른 글

CREATE  (0) 2026.02.10
String, Date  (0) 2026.02.09
JOIN  (0) 2026.02.08
SUM, MAX, MIN  (0) 2026.02.07
IS NULL  (0) 2026.02.07