10 SQL SELECT 세부 명령어 실습 2
1. 인트로
WHERE 절의 심화 기능인 SubQuery(서브쿼리), LIMIT & BETWEEN(리밋 앤 비트윈)을 익힌다. 이 기능들은 복잡한 조건의 데이터를 정밀하게 추출할 때 사용된다.
2. 실습 DB 준비 (9차시 webtoon.db 이어서 사용)
1import sqlite32import pandas as pd3
4conn = sqlite3.connect("webtoon.db")5cur = conn.cursor()6
7# 데이터 확인8df = pd.read_sql("SELECT * FROM webtoons", conn)9print(df[["id","title","genre","score","views"]])3. WHERE 절에서의 SubQuery(서브쿼리) 활용 실습
3.1. SubQuery(서브쿼리)란?
쿼리(Query(쿼리)) 안에 또 다른 쿼리를 넣는 방식이다.
괄호 () 안의 쿼리가 먼저 실행되고, 그 결과를 바깥 쿼리에서 사용한다.
1# 평균 점수보다 높은 웹툰 목록2df = pd.read_sql("""3 SELECT title, score4 FROM webtoons5 WHERE score > (SELECT AVG(score) FROM webtoons)6 ORDER BY score DESC7""", conn)8print("=== 평균 점수 초과 웹툰 ===")9print(df)코드 설명:
- 5행:
(SELECT AVG(score) FROM webtoons)— 먼저 전체 평균 점수를 계산한다. - 5행: 바깥 WHERE 절에서 그 평균값보다 높은 행만 가져온다.
AVG()는 평균을 계산하는 집계 함수이다.
3.2. 최고 조회수 웹툰 조회
1# 조회수가 가장 높은 웹툰의 정보 조회2cur.execute("""3 SELECT title, author, views4 FROM webtoons5 WHERE views = (SELECT MAX(views) FROM webtoons)6""")7top = cur.fetchone()8print(f"최고 조회수 웹툰: {top[0]} ({top[1]}) - {top[2]:,}회")코드 설명:
- 5행:
MAX(views)— views 중 가장 큰 값을 구하는 서브쿼리이다. - 바깥 WHERE에서 그 최대값과 일치하는 행을 가져온다.
- 8행:
{top[2]:,}— 숫자에 천 단위 콤마를 추가하여 출력한다.
3.3. 카카오페이지 웹툰 중 평균보다 높은 점수
1df = pd.read_sql("""2 SELECT title, platform, score3 FROM webtoons4 WHERE platform = '카카오페이지'5 AND score > (6 SELECT AVG(score)7 FROM webtoons8 WHERE platform = '카카오페이지'9 )10 ORDER BY score DESC11""", conn)12print("=== 카카오페이지 평균 초과 웹툰 ===")13print(df)코드 설명:
- 4행: 먼저 platform이 ‘카카오페이지’인 것만 필터링한다.
- 5~9행: 서브쿼리에서 카카오페이지 웹툰들의 평균 점수를 구한다.
- 같은 플랫폼 내 평균보다 점수가 높은 웹툰만 결과로 나온다.
4. LIMIT & BETWEEN(리밋 앤 비트윈) 명령어의 이해 및 실습
4.1. LIMIT(리밋) & OFFSET(오프셋)
1# 상위 3개만 가져오기2df = pd.read_sql(3 "SELECT title, score FROM webtoons ORDER BY score DESC LIMIT 3",4 conn5)6print("=== TOP 3 ===")7print(df)1# 4위~6위 (OFFSET으로 앞 3개를 건너뛴다)2df = pd.read_sql(3 "SELECT title, score FROM webtoons ORDER BY score DESC LIMIT 3 OFFSET 3",4 conn5)6print("=== 4위~6위 ===")7print(df)코드 설명:
LIMIT 3— 결과의 앞에서 3개만 가져온다.OFFSET 3— 앞의 3개를 건너뛰고 그 다음부터 가져온다.- 리액트 앱의 페이지네이션(Pagination(페이지네이션)) 기능에 핵심적으로 사용된다.
1# 페이지네이션 함수 예시2def get_page(page=1, per_page=3):3 offset = (page - 1) * per_page4 df = pd.read_sql(5 f"SELECT title, score FROM webtoons ORDER BY score DESC LIMIT {per_page} OFFSET {offset}",6 conn7 )8 return df9
10print("=== 1페이지 ===")11print(get_page(1))12print("=== 2페이지 ===")13print(get_page(2))코드 설명:
- 2행:
offset = (page - 1) * per_page— 1페이지는 0번째부터, 2페이지는 3번째부터 시작한다. - 이 함수가 리액트 앱의 페이지네이션 API 핵심 로직이다.
4.2. BETWEEN(비트윈) — 범위 검색
1# 점수 9.0 이상 9.6 이하 웹툰2df = pd.read_sql("""3 SELECT title, score4 FROM webtoons5 WHERE score BETWEEN 9.0 AND 9.66 ORDER BY score DESC7""", conn)8print("=== 점수 9.0~9.6 ===")9print(df)코드 설명:
- 5행:
BETWEEN 9.0 AND 9.6— 9.0 이상 9.6 이하(양쪽 포함)인 값을 가져온다. WHERE score >= 9.0 AND score <= 9.6과 동일하지만 더 간결하다.
1# NOT BETWEEN — 범위 밖의 값2df = pd.read_sql("""3 SELECT title, views4 FROM webtoons5 WHERE views NOT BETWEEN 20000000 AND 400000006 ORDER BY views DESC7""", conn)8print("=== 2000만~4000만 조회수 제외 ===")9print(df)코드 설명:
NOT BETWEEN— 범위 안에 포함되지 않는 값을 가져온다.
5. 복합 실전 실습
1# 조회수 TOP 5 중 점수 9.0 이상인 웹툰2df = pd.read_sql("""3 SELECT title, score, views4 FROM webtoons5 WHERE id IN (6 SELECT id FROM webtoons ORDER BY views DESC LIMIT 57 )8 AND score >= 9.09 ORDER BY score DESC10""", conn)11print("=== 조회수 TOP5 & 점수 9.0+ ===")12print(df)코드 설명:
- 5~7행: 서브쿼리로 조회수 상위 5개의 id 목록을 먼저 구한다.
- 4행: 그 id 목록 안에 포함된 웹툰만 필터링한다.
- 8행: 그중 점수 9.0 이상인 것만 최종 결과로 가져온다.
6. 활용 Plus — 리액트 무한 스크롤 구현
리액트 앱의 **무한 스크롤(Infinite Scroll(인피니트 스크롤))**은 LIMIT + OFFSET 조합으로 구현된다.
1# 스크롤할 때마다 다음 5개씩 불러오기2def load_more(offset=0, limit=5):3 cur.execute("""4 SELECT title, score, views5 FROM webtoons6 ORDER BY views DESC7 LIMIT ? OFFSET ?8 """, (limit, offset))9 return cur.fetchall()10
11# 첫 번째 로드 (0번부터 5개)12batch1 = load_more(offset=0)13print("첫 번째:", batch1)14
15# 두 번째 로드 (5번부터 5개)16batch2 = load_more(offset=5)17print("두 번째:", batch2)코드 설명:
- 2행:
offset과limit을 인자로 받는다. - 7행:
?플레이스홀더로 LIMIT과 OFFSET 값을 안전하게 전달한다. - 리액트에서 스크롤 이벤트가 발생할 때마다 offset을 증가시켜 다음 데이터를 로드한다.
7. 문제풀기
SQL 문장 안에 포함된 또 다른 SQL 문장이다. 괄호 안의 쿼리가 먼저 실행되고, 그 결과를 바깥 쿼리에서 조건 값으로 사용한다.
결과를 정렬한 뒤 앞의 10개를 건너뛰고, 11번째부터 5개를 가져온다. 3페이지(페이지당 5개)의 데이터를 가져올 때 사용하는 패턴이다.
WHERE 컬럼 >= 10 AND 컬럼 <= 20
BETWEEN은 양쪽 경계값을 포함한다.
OFFSET = (3 - 1) * 4 = 8
LIMIT 4 OFFSET 8