1 문제 1: 경제 분야 도서와 저자 조회
1.1 문제 정보
| 항목 | 내용 |
|---|---|
| 출처 | 튜터 생성 (앵커: Programmers “없어진 기록 찾기” Lv.3, 앵커: Programmers “조건에 맞는 도서와 저자 리스트 출력하기” Lv.2) |
| 난이도 | Level 2 |
| SQL 유형 | INNER JOIN |
1.2 문제 설명
CREATE TABLE books (
book_id INT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
author_id INT NOT NULL,
category VARCHAR(50) NOT NULL,
published_date DATE NOT NULL
);
CREATE TABLE authors (
author_id INT PRIMARY KEY,
author_name VARCHAR(50) NOT NULL
);
INSERT INTO authors VALUES
(1, '홍길동'), (2, '이경제'), (3, '김무역'), (4, '박소설');
INSERT INTO books VALUES
(1, '경제의 이해', 2, '경제', '2020-03-15'),
(2, '소설 한 편', 4, '소설', '2021-07-20'),
(3, '무역 기초', 3, '경제', '2019-09-01'),
(4, '문학의 세계', 1, '인문', '2022-01-10'),
(5, '투자 전략', 2, '경제', '2023-06-30');경제 카테고리 도서의 book_id, title, author_name을 조회하라. - 정렬: published_date 오름차순
기대 결과:
| book_id | title | author_name |
|---|---|---|
| 3 | 무역 기초 | 김무역 |
| 1 | 경제의 이해 | 이경제 |
| 5 | 투자 전략 | 이경제 |
1.3 최종 풀이
SELECT b.book_id, b.title, a.author_name
FROM books b
INNER JOIN authors a ON b.author_id = a.author_id
WHERE b.category = '경제'
ORDER BY b.published_date ASC;FROM books b INNER JOIN authors a ON b.author_id = a.author_id: JOIN이 FROM 절에서 완성된 후 WHERE가 적용된다b.author_id = a.author_id: 두 테이블을 연결하는 조인 키WHERE b.category = '경제': JOIN 완료 후 필터링b.title,a.author_name: 컬럼이 어느 테이블에서 오는지 별칭으로 명시한다
1.4 Tips
- 핵심 개념:
FROM 테이블1 INNER JOIN 테이블2 ON 조인키— JOIN은 FROM 절에서 완성 - 함정 1: WHERE 절을 FROM/JOIN보다 앞에 쓰면 문법 오류. SQL 실행 순서:
FROM → JOIN → WHERE → SELECT - 함정 2:
author_name이 books 테이블에 없는 컬럼임에도b.author_name으로 참조하면 오류. 컬럼이 어느 테이블 소속인지 확인 후 올바른 별칭을 붙인다 - 팁: 별칭(
b,a)을 짧게 쓰면 가독성이 높아지고 컬럼 귀속 실수를 줄인다
2 문제 2: 히트 앨범 목록 집계
2.1 문제 정보
| 항목 | 내용 |
|---|---|
| 출처 | 튜터 생성 (앵커: Programmers “주문량이 많은 아이스크림들 조회하기” 계열, SQL Lv.2~4, 정답률 ~65%) |
| 난이도 | Level 2 |
| SQL 유형 | INNER JOIN + 다중 키 ORDER BY — 경계값·타입 일치 함정 |
2.2 문제 설명
어느 음반 유통사가 최근 시장 분석 자료를 준비하고 있다. 비교적 최근에 발매된 “히트 앨범”만 추려 아티스트 정보와 함께 정리한 목록이 필요하다.
CREATE TABLE ARTISTS (
ARTIST_ID INT PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
DEBUT_YEAR INT,
COUNTRY VARCHAR(30)
);
CREATE TABLE ALBUMS (
ALBUM_ID INT PRIMARY KEY,
ARTIST_ID INT,
TITLE VARCHAR(100) NOT NULL,
RELEASE_YEAR INT,
SALES INT
);샘플 데이터 (공개)
INSERT INTO ARTISTS VALUES
(1, 'Aurora', 2015, 'KR'),
(2, 'Blue Note', 2010, 'US'),
(3, 'Crescent', 2018, 'JP'),
(4, 'Dawn Choir', 2019, 'KR'),
(5, 'Echo Wave', 2021, 'UK'),
(6, 'Falling Sky', 2016, 'US');
INSERT INTO ALBUMS VALUES
(101, 1, 'First Light', 2019, 80000),
(102, 1, 'Second Dawn', 2021, 120000),
(103, 2, 'Midnight Blues', 2022, 55000),
(104, 3, 'Tides', 2020, 50000),
(105, 4, 'Choral Hymns', 2020, 30000),
(106, 5, 'Radio Signal', 2023, 120000);제한사항
- 2020년 이후(포함) 발매 & 판매량 5만 장 이상인 앨범의
title,release_year,sales,artist_name을 조회한다. - 정렬: 판매량 내림차순 → 동일 시 발매연도 오름차순 → 동일 시 앨범 제목 사전 오름차순.
- 아티스트 마스터에 존재하지 않는
artist_id를 가진 앨범은 제외한다.
기대 결과 (공개 샘플 기준)
| TITLE | RELEASE_YEAR | SALES | ARTIST_NAME |
|---|---|---|---|
| Second Dawn | 2021 | 120000 | Aurora |
| Radio Signal | 2023 | 120000 | Echo Wave |
| Midnight Blues | 2022 | 55000 | Blue Note |
| Tides | 2020 | 50000 | Crescent |
2.3 최종 풀이
SELECT al.title, al.release_year, al.sales, ar.name AS artist_name
FROM albums al
INNER JOIN artists ar
ON al.artist_id = ar.artist_id
WHERE al.release_year >= 2020
AND al.sales >= 50000
ORDER BY al.sales DESC, al.release_year ASC, al.title ASC;INNER JOIN ... ON al.artist_id = ar.artist_id: 미등록 artist를 가진 앨범은 매칭 실패로 자연 제외 — 별도 NULL 방어 불필요WHERE al.release_year >= 2020:INT컬럼과 정수 리터럴 비교 (타입 일치)ORDER BY ... DESC, ... ASC, ... ASC: 3단계 정렬 방향 모두 명시
2.4 Tips
- 타입 일치 (핵심 함정):
RELEASE_YEAR는INT컬럼.'2020-01-01'처럼 문자열 리터럴을 비교하면 MySQL은 암묵 변환으로2020이 되어 우연히 동작하나 다음 위험이 있다:STRICT_TRANS_TABLES모드에서 경고·에러 발생 가능'2020-13-99'같은 오타도2020으로 파싱되어 실수를 감춤- 컬럼에 인덱스가 있어도 Range Scan이 아닌 Full Scan으로 떨어질 수 있음
- 컬럼 타입을 스키마에서 먼저 확인 후 리터럴 타입을 맞춘다 (
>= 2020)
- INNER JOIN과 NULL 방어 불필요:
ar.artist_id IS NOT NULL같은 조건은 INNER JOIN 뒤에서 항상 TRUE.artists.artist_id가 PK라 원본에서도 NULL 불가. 이는 NOT IN의 NULL 방어 습관이 다른 맥락에 잘못 전이된 “패턴 과일반화” - 경계값 해석: “이후”는 포함 여부가 문맥 의존. 문제가 “2020년 이후”를 요구하면
>= 2020이 표준 해석. 의심될 때는 기대 결과 테이블로 역추론 (2020년 앨범 포함 여부 확인) - 다중 키 ORDER BY:
키1 방향, 키2 방향, 키3 방향쉼표 구분. 방향 생략 시 ASC 기본값이나 명시가 안전.AND로 연결하는 실수 주의 (쉼표가 정답)
2.5 면접 방어 포인트
- 실행 계획 분석:
albums.artist_id인덱스가 있으면 Index Nested Loop Join. 없으면 Hash Join (MySQL 8.0+) 또는 Block Nested Loop. 필터release_year >= 2020 AND sales >= 50000은 복합 인덱스(release_year, sales)또는(sales, release_year)중 선택도 높은 컬럼을 선행에 두는 전략. ORDER BY 3단계는(sales DESC, release_year ASC, title ASC)Descending Index (MySQL 8.0+)로 Filesort 제거 가능 - 대안 비교:
- INNER JOIN + 필터 (채택): 미등록 artist 자연 제외, 의도 표현 명확, 옵티마이저 친화적
- LEFT JOIN +
ar.artist_id IS NOT NULL: 옵티마이저가 INNER로 rewrite하나 의도 전달 모호, 가독성 저하 - EXISTS 기반:
artist_name컬럼 참조가 필요하여 본 문제엔 부적합 (EXISTS는 외부에서 서브쿼리 컬럼 참조 불가)
- 공학적 확장:
- 대용량: 앨범 수억 건이면
(release_year, sales, artist_id)커버링 인덱스로 JOIN 전 필터링 집중 - 방언 호환:
INNER JOIN ... ON은 표준 SQL. Oracle(+)오래된 문법은 지양. PostgreSQL·MySQL·SQL Server 모두 동일 - 동점 처리 정책: 실전에서는 비즈니스 규칙(예: 최근 발매일 우선 → 동일 시 알파벳 순)을 쿼리 주석으로 문서화하여 유지보수자가 의도 파악 가능하게
- 대용량: 앨범 수억 건이면