🐨CoalaCoding
DocsExamplesTry itBoardB반B반
🐨CoalaCoding

개발자를 위한 한국어 웹 기술 문서

문서

  • JavaScript
  • Web Publishing
  • React
  • Python

커뮤니티

  • 게시판
  • 예제 모음
  • Try it 에디터

기타

  • GitHub
  • 관리자
© 2026 CoalaCoding. All rights reserved.
  • sqlite3로 SQL 명령문 실행
  • DB데이터 일괄추가하기
  • SQLite3 라이브러리를 활용한 SQL 명령문 실행
  • Pandas를 활용한 SQL 명령문 실행
  • CREATE 명령어를 활용한 DB 테이블의 생성
  • DDL 명령어의 이해 및 실습
  • SQL SELECT 명령어의 이해 및 실습
  • SQL INSERT, UPDATE, DELETE 명령어 실습
  • SQL SELECT 세부 명령어 실습 1
  • SQL SELECT 세부 명령어 실습 2
  • SQL INNER JOIN 실습
  • SQL LEFT JOIN 실습
  • SQL GROUP BY 실습 1
  • SQL GROUP BY 실습 2
  • SQL SubQuery 활용 실습
  • 데이터셋 기반 DB 구축 & DB 활용 실습 — 리액트 연동 완성
  1. 홈
  2. 문서
  3. Backend
  4. Database & SQL
  5. SQL SELECT 세부 명령어 실습 2

SQL SELECT 세부 명령어 실습 2

코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.

구문

1. 인트로

WHERE 절의 심화 기능인 SubQuery(서브쿼리), LIMIT & BETWEEN(리밋 앤 비트윈)을 익힌다. 이 기능들은 복잡한 조건의 데이터를 정밀하게 추출할 때 사용된다.


2. 실습 DB 준비 (9차시 webtoon.db 이어서 사용)

import sqlite3
import pandas as pd

conn = sqlite3.connect("webtoon.db")
cur  = conn.cursor()

# 데이터 확인
df = pd.read_sql("SELECT * FROM webtoons", conn)
print(df[["id","title","genre","score","views"]])

3. WHERE 절에서의 SubQuery(서브쿼리) 활용 실습

3.1. SubQuery(서브쿼리)란?

쿼리(Query(쿼리)) 안에 또 다른 쿼리를 넣는 방식이다. 괄호 () 안의 쿼리가 먼저 실행되고, 그 결과를 바깥 쿼리에서 사용한다.

# 평균 점수보다 높은 웹툰 목록
df = pd.read_sql("""
    SELECT title, score
    FROM webtoons
    WHERE score > (SELECT AVG(score) FROM webtoons)
    ORDER BY score DESC
""", conn)
print("=== 평균 점수 초과 웹툰 ===")
print(df)

코드 설명:

  • 5행: (SELECT AVG(score) FROM webtoons) — 먼저 전체 평균 점수를 계산한다.
  • 5행: 바깥 WHERE 절에서 그 평균값보다 높은 행만 가져온다.
  • AVG()는 평균을 계산하는 집계 함수이다.

3.2. 최고 조회수 웹툰 조회

# 조회수가 가장 높은 웹툰의 정보 조회
cur.execute("""
    SELECT title, author, views
    FROM webtoons
    WHERE views = (SELECT MAX(views) FROM webtoons)
""")
top = cur.fetchone()
print(f"최고 조회수 웹툰: {top[0]} ({top[1]}) - {top[2]:,}회")

코드 설명:

  • 5행: MAX(views) — views 중 가장 큰 값을 구하는 서브쿼리이다.
  • 바깥 WHERE에서 그 최대값과 일치하는 행을 가져온다.
  • 8행: {top[2]:,} — 숫자에 천 단위 콤마를 추가하여 출력한다.

3.3. 카카오페이지 웹툰 중 평균보다 높은 점수

df = pd.read_sql("""
    SELECT title, platform, score
    FROM webtoons
    WHERE platform = '카카오페이지'
      AND score > (
          SELECT AVG(score)
          FROM webtoons
          WHERE platform = '카카오페이지'
      )
    ORDER BY score DESC
""", conn)
print("=== 카카오페이지 평균 초과 웹툰 ===")
print(df)

코드 설명:

  • 4행: 먼저 platform이 '카카오페이지'인 것만 필터링한다.
  • 5~9행: 서브쿼리에서 카카오페이지 웹툰들의 평균 점수를 구한다.
  • 같은 플랫폼 내 평균보다 점수가 높은 웹툰만 결과로 나온다.

4. LIMIT & BETWEEN(리밋 앤 비트윈) 명령어의 이해 및 실습

4.1. LIMIT(리밋) & OFFSET(오프셋)

# 상위 3개만 가져오기
df = pd.read_sql(
    "SELECT title, score FROM webtoons ORDER BY score DESC LIMIT 3",
    conn
)
print("=== TOP 3 ===")
print(df)
# 4위~6위 (OFFSET으로 앞 3개를 건너뛴다)
df = pd.read_sql(
    "SELECT title, score FROM webtoons ORDER BY score DESC LIMIT 3 OFFSET 3",
    conn
)
print("=== 4위~6위 ===")
print(df)

코드 설명:

  • LIMIT 3 — 결과의 앞에서 3개만 가져온다.
  • OFFSET 3 — 앞의 3개를 건너뛰고 그 다음부터 가져온다.
  • 리액트 앱의 페이지네이션(Pagination(페이지네이션)) 기능에 핵심적으로 사용된다.
# 페이지네이션 함수 예시
def get_page(page=1, per_page=3):
    offset = (page - 1) * per_page
    df = pd.read_sql(
        f"SELECT title, score FROM webtoons ORDER BY score DESC LIMIT {per_page} OFFSET {offset}",
        conn
    )
    return df

print("=== 1페이지 ===")
print(get_page(1))
print("=== 2페이지 ===")
print(get_page(2))

코드 설명:

  • 2행: offset = (page - 1) * per_page — 1페이지는 0번째부터, 2페이지는 3번째부터 시작한다.
  • 이 함수가 리액트 앱의 페이지네이션 API 핵심 로직이다.

4.2. BETWEEN(비트윈) — 범위 검색

# 점수 9.0 이상 9.6 이하 웹툰
df = pd.read_sql("""
    SELECT title, score
    FROM webtoons
    WHERE score BETWEEN 9.0 AND 9.6
    ORDER BY score DESC
""", conn)
print("=== 점수 9.0~9.6 ===")
print(df)

코드 설명:

  • 5행: BETWEEN 9.0 AND 9.6 — 9.0 이상 9.6 이하(양쪽 포함)인 값을 가져온다.
  • WHERE score >= 9.0 AND score <= 9.6과 동일하지만 더 간결하다.
# NOT BETWEEN — 범위 밖의 값
df = pd.read_sql("""
    SELECT title, views
    FROM webtoons
    WHERE views NOT BETWEEN 20000000 AND 40000000
    ORDER BY views DESC
""", conn)
print("=== 2000만~4000만 조회수 제외 ===")
print(df)

코드 설명:

  • NOT BETWEEN — 범위 안에 포함되지 않는 값을 가져온다.

5. 복합 실전 실습

# 조회수 TOP 5 중 점수 9.0 이상인 웹툰
df = pd.read_sql("""
    SELECT title, score, views
    FROM webtoons
    WHERE id IN (
        SELECT id FROM webtoons ORDER BY views DESC LIMIT 5
    )
    AND score >= 9.0
    ORDER BY score DESC
""", conn)
print("=== 조회수 TOP5 & 점수 9.0+ ===")
print(df)

코드 설명:

  • 5~7행: 서브쿼리로 조회수 상위 5개의 id 목록을 먼저 구한다.
  • 4행: 그 id 목록 안에 포함된 웹툰만 필터링한다.
  • 8행: 그중 점수 9.0 이상인 것만 최종 결과로 가져온다.

6. 활용 Plus — 리액트 무한 스크롤 구현

리액트 앱의 **무한 스크롤(Infinite Scroll(인피니트 스크롤))**은 LIMIT + OFFSET 조합으로 구현된다.

# 스크롤할 때마다 다음 5개씩 불러오기
def load_more(offset=0, limit=5):
    cur.execute("""
        SELECT title, score, views
        FROM webtoons
        ORDER BY views DESC
        LIMIT ? OFFSET ?
    """, (limit, offset))
    return cur.fetchall()

# 첫 번째 로드 (0번부터 5개)
batch1 = load_more(offset=0)
print("첫 번째:", batch1)

# 두 번째 로드 (5번부터 5개)
batch2 = load_more(offset=5)
print("두 번째:", batch2)

코드 설명:

  • 2행: offset과 limit을 인자로 받는다.
  • 7행: ? 플레이스홀더로 LIMIT과 OFFSET 값을 안전하게 전달한다.
  • 리액트에서 스크롤 이벤트가 발생할 때마다 offset을 증가시켜 다음 데이터를 로드한다.

7. 문제풀기

Q1. SubQuery(서브쿼리)란 무엇인가?

SQL 문장 안에 포함된 또 다른 SQL 문장이다. 괄호 안의 쿼리가 먼저 실행되고, 그 결과를 바깥 쿼리에서 조건 값으로 사용한다.

Q2. LIMIT 5 OFFSET 10의 의미는?

결과를 정렬한 뒤 앞의 10개를 건너뛰고, 11번째부터 5개를 가져온다. 3페이지(페이지당 5개)의 데이터를 가져올 때 사용하는 패턴이다.

Q3. BETWEEN 10 AND 20을 AND 조건식으로 변환하시오.

WHERE 컬럼 >= 10 AND 컬럼 <= 20 BETWEEN은 양쪽 경계값을 포함한다.

Q4. 페이지네이션에서 3페이지(페이지당 4개)의 OFFSET 값은?

OFFSET = (3 - 1) * 4 = 8 LIMIT 4 OFFSET 8

목차

  • 구문