SQL 공부(1)

김우진·2026년 4월 4일

DataBase

목록 보기
3/3

SQL 연습문제 정리 (Q3.1)

마당서점 데이터베이스를 이용한 SQL 기초 쿼리 연습


📦 사용 테이블

Book

bookidbooknamepublisherprice
1축구의 역사굿스포츠7000
2축구 아는 여자나무수13000
3축구의 이해대한미디어22000
4골프 바이블대한미디어35000
5피겨 교본굿스포츠8000
6배구 단계별기술굿스포츠6000
7야구의 추억이상미디어20000
8야구를 부탁해이상미디어13000
9올림픽 이야기삼성당7500
10Olympic ChampionsPearson13000

Orders

orderidcustidbookidsalepriceorderdate
11160002024-07-01
213210002024-07-03
32580002024-07-03
43660002024-07-04
547200002024-07-05
612120002024-07-07
748130002024-07-07
8310120002024-07-08
921070002024-07-09
1038130002024-07-10

Customer

custidnameaddressphone
1박지성영국 맨체스타000-5000-0001
2김연아대한민국 서울000-6000-0001
3김연경대한민국 경기도000-7000-0001
4추신수미국 클리블랜드000-8000-0001
5박세리대한민국 대전NULL

01 고객 요구 질문

1.1 도서번호가 1인 도서의 이름

SELECT bookname FROM book
WHERE bookid = 1;

1.2 가격이 20,000원 이상인 도서의 이름

SELECT bookname FROM book
WHERE price >= 20000;

1.3 박지성의 총구매액

SELECT SUM(saleprice) FROM orders
WHERE custid = 1;

박지성의 custid를 모를 경우 서브쿼리 사용

SELECT SUM(saleprice) FROM orders
WHERE custid = (SELECT custid FROM customer WHERE name = '박지성');

1.4 박지성이 구매한 도서의 수

SELECT COUNT(*) FROM orders
WHERE custid = 1;

1.5 박지성이 구매한 도서의 출판사 수

SELECT COUNT(DISTINCT b.publisher)
FROM orders o JOIN book b ON o.bookid = b.bookid
WHERE o.custid IN (SELECT custid FROM customer WHERE name = '박지성');

= vs IN 차이

  • = : 서브쿼리 결과가 1개일 때만 사용 가능, 2개 이상이면 오류
  • IN : 결과가 여러 개여도 정상 작동 → 동명이인 등을 고려하면 IN이 더 안전

1.6 박지성이 구매한 도서의 이름, 가격, 정가와 판매가격의 차이

SELECT b.bookname, b.price, o.saleprice, (b.price - o.saleprice) AS difference
FROM orders o JOIN book b ON o.bookid = b.bookid
WHERE o.custid IN (SELECT custid FROM customer WHERE name = '박지성');
booknamepricesalepricedifference
축구의 역사700060001000
축구의 이해22000210001000
축구 아는 여자13000120001000

1.7 박지성이 구매하지 않은 도서의 이름

SELECT bookname
FROM book
WHERE bookid NOT IN (
    SELECT bookid FROM orders
    WHERE custid IN (SELECT custid FROM customer WHERE name = '박지성')
);

⚠️ 주의: FROM orders JOIN book으로 시작하면 안 됨

  • "박지성이 구매하지 않은 도서" → 기준이 전체 도서(book)
  • FROM book에서 시작해서 박지성의 주문 목록을 NOT IN으로 제외해야 함
  • FROM orders로 시작하면 "다른 사람들이 주문한 책"이 나와버려 결과가 틀림

02 운영자·경영자 요구 질문

2.1 마당서점 도서의 총개수

SELECT COUNT(*) FROM book;

2.2 마당서점에 도서를 출고하는 출판사의 총개수

SELECT COUNT(DISTINCT publisher) FROM book;

DISTINCT 없이 쓰면 중복 출판사가 여러 번 카운트됨

2.3 모든 고객의 이름, 주소

SELECT name, address FROM customer;

2.4 2024년 7월 4일~7월 7일 사이에 주문받은 도서의 주문번호

SELECT orderid FROM orders
WHERE orderdate BETWEEN '2024-07-04' AND '2024-07-07';

⚠️ IN ('2024-07-04', '2024-07-07')은 4일과 7일만 조회됨, 사이 날짜 누락

2.5 2024년 7월 4일~7월 7일 사이에 주문받은 도서를 제외한 도서의 주문번호

SELECT orderid FROM orders
WHERE orderdate NOT BETWEEN '2024-07-04' AND '2024-07-07';

2.6 성이 '김' 씨인 고객의 이름과 주소

SELECT name, address FROM customer
WHERE name LIKE '김%';

2.7 성이 '김' 씨이고 이름이 '아'로 끝나는 고객의 이름과 주소

SELECT name, address FROM customer
WHERE name LIKE '김%아';

⚠️ '김_아'는 이름이 3글자(김X아)인 경우만 해당됨

  • _ : 딱 1글자만 대체
  • % : 0글자 이상 대체 → 4글자 이상 이름도 포함

2.8 주문하지 않은 고객의 이름 (부속질의 사용)

SELECT name FROM customer
WHERE custid NOT IN (SELECT custid FROM orders);

2.9 주문 금액의 총액과 주문의 평균 금액

SELECT SUM(saleprice) AS 총액, AVG(saleprice) AS 평균금액
FROM orders;

2.10 고객의 이름과 고객별 구매액

SELECT c.name, SUM(o.saleprice) AS 구매액
FROM customer c JOIN orders o ON c.custid = o.custid
GROUP BY c.custid, c.name;

"고객별" 이라는 단어가 나오면 GROUP BY 사용

GROUP BY c.custid, c.name 으로 쓰는 이유: 동명이인이 있을 경우 이름만으로 묶으면 합산될 수 있어서 custid를 함께 명시하는 것이 안전

2.11 고객의 이름과 고객이 구매한 도서 목록

SELECT c.name, b.bookname
FROM customer c
JOIN orders o ON c.custid = o.custid
JOIN book b ON o.bookid = b.bookid;

customer와 book은 직접 연결되는 컬럼이 없음
orders가 중간 테이블 역할 → JOIN을 두 번 해야 함

customer --- orders --- book
   custid      custid
               bookid   bookid

2.12 도서의 가격(Book)과 판매가격(Orders)의 차이가 가장 많은 주문

SELECT b.bookname, b.price, o.saleprice, (b.price - o.saleprice) AS difference
FROM book b JOIN orders o ON b.bookid = o.bookid
WHERE (b.price - o.saleprice) = (
    SELECT MAX(b.price - o.saleprice)
    FROM book b JOIN orders o ON b.bookid = o.bookid
);

MAX()만 쓰면 최댓값 숫자만 나옴
해당 주문 정보 전체를 가져오려면 서브쿼리로 최댓값을 구한 뒤 WHERE로 해당 행을 찾아야 함

2.13 도서의 판매액 평균보다 자신의 구매액 평균이 더 높은 고객의 이름

SELECT c.name
FROM customer c JOIN orders o ON c.custid = o.custid
GROUP BY c.custid, c.name
HAVING AVG(o.saleprice) > (SELECT AVG(saleprice) FROM orders);

WHERE vs HAVING

구분사용 위치
일반 조건WHERE
집계함수 조건HAVING

HAVING은 항상 GROUP BY 뒤에 위치


💡 핵심 개념 정리

FROM 테이블 선택 기준

질문 패턴기준 테이블
"~한 것들 중에서 집계"행위 테이블 (orders)
"전체 중에서 ~한/안한 것"대상 테이블 (book, customer)

핵심: "무엇을" 조회하는지가 FROM/기준 테이블을 결정한다

LIKE 패턴

패턴의미
%0글자 이상 임의의 문자
_정확히 1글자

집계함수와 GROUP BY

  • COUNT(*) : NULL 포함 전체 행 수
  • COUNT(컬럼) : NULL 제외 행 수
  • COUNT(DISTINCT 컬럼) : 중복 제외 고유값 수
  • 집계함수 조건은 반드시 HAVING 사용

서브쿼리

-- 단일 결과 → = 사용 가능
WHERE custid = (SELECT custid FROM customer WHERE name = '박지성')

-- 복수 결과 가능성 → IN 사용 권장
WHERE custid IN (SELECT custid FROM customer WHERE name = '박지성')

0개의 댓글