데이터 범위 읽기 - LIMIT와 OFFSET
데이터 범위 읽기 - LIMIT와 OFFSET (Read a Range of Data - LIMIT and OFFSET)
이제 여러분은 .one(), .first(), session.get()으로 행 하나를 가져오는 법을 알아요.
그리고 .where()로 필터링하면서 여러 행을 가져오는 법도 알죠.
이제 결과의 범위(range) 만 가져오는 법을 볼게요.
출처: 공식문서
데이터 만들기
이전과 같은 코드로 계속하되, 예제를 단순하게 유지하고 여기서 하고 싶은 것에 집중하기 위해 select_heroes() 함수는 조금 바꿀게요.
다시, 조회할 데이터가 있도록 히어로 여러 명을 만들 거예요:
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32)
hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35)
hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36)
hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93)
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.add(hero_4)
session.add(hero_5)
session.add(hero_6)
session.add(hero_7)
session.commit()
모두 조회하는 코드 복습하기
select() 예제들에서 모든 히어로를 조회할 때 썼던 코드는 이거였어요:
def select_heroes():
with Session(engine) as session:
statement = select(Hero)
results = session.exec(statement)
heroes = results.all()
print(heroes)
하지만 이건 데이터베이스에 수천 명이 있을 수 있는데 전부 한 번에 가져오는 거라 문제가 될 수 있어요.
LIMIT로 조회하기
지금 데이터베이스에는 히어로가 7명 있어요. 하지만 수천 명일 수도 있으니, 처음 3명만 가져오도록 결과를 제한해 볼게요:
def select_heroes():
with Session(engine) as session:
statement = select(Hero).limit(3)
results = session.exec(statement)
heroes = results.all()
print(heroes)
select()가 주는 특별한 select 객체에는 결과를 특정 개수로 제한하는 데 쓸 수 있는 .limit() 메서드도 있어요.
이 경우 7개 행을 전부 가져오는 대신, 처음 3개만 가져오도록 제한했어요.
커맨드 라인에서 프로그램 실행하기
커맨드 라인에서 실행하면 이렇게 출력돼요:
$ uv run python app.py
// 이전 출력은 생략했어요 🙈
// LIMIT로 SELECT
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
LIMIT ? OFFSET ?
INFO Engine [no key 0.00014s] (3, 0)
// 받은 히어로들을 프린트, 딱 3명
[
Hero(age=None, secret_name='Dive Wilson', id=1, name='Deadpond'),
Hero(age=None, secret_name='Pedro Parqueador', id=2, name='Spider-Boy'),
Hero(age=48, secret_name='Tommy Sharp', id=3, name='Rusty-Man')
]
좋아요! 원하는 대로 히어로 3명만 가져왔네요.
!!! tip "팁"
그 SQL 코드는 잠시 후에 더 자세히 볼게요.
OFFSET과 LIMIT로 조회하기
이제 결과를 제한해서 처음 3명만 가져올 수 있어요.
하지만 사용자 인터페이스가 히어로를 3명씩 묶어서 보여준다고 상상해 볼게요.
!!! tip "팁"
이걸 보통 "**페이지네이션(pagination)**"이라고 불러요. 사용자 인터페이스가 한 번에 미리 정해진 수의 히어로 "페이지"를 보여주기 때문이에요.
그리고 사용자가 인터페이스와 상호작용해서 다음 페이지로 넘어가는 식이죠.
다음 3명은 어떻게 가져올까요?
.offset()을 쓰면 돼요:
def select_heroes():
with Session(engine) as session:
statement = select(Hero).offset(3).limit(3)
results = session.exec(statement)
heroes = results.all()
print(heroes)
동작 방식은 이래요. select()가 주는 특별한 select 객체에는 .where(), .offset(), .limit() 같은 메서드들이 있어요.
각 메서드는 내부의 특별한 select 문장 객체에 변경을 적용하면서 같은 객체를 반환해요. 그래서 위 예제처럼 .offset()과 .limit()을 둘 다 쓰는 것처럼, 그 객체에 메서드를 계속 붙일 수 있어요.
Offset은 "이만큼 행을 건너뛰어라"라는 뜻이에요. 이미 봤던 처음 3개를 건너뛰고 싶으니 .offset(3)을 쓰는 거죠.
OFFSET을 써서 커맨드 라인에서 실행하기
이제 커맨드 라인에서 프로그램을 실행하면 이렇게 출력돼요:
$ python app.py
// 이전 출력은 생략했어요 🙈
// LIMIT와 OFFSET으로 SELECT
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
LIMIT ? OFFSET ?
INFO Engine [no key 0.00020s] (3, 3)
// 받은 히어로 3명을 프린트, 두 번째 묶음
[
Hero(age=32, secret_name='Natalia Roman-on', id=4, name='Tarantula'),
Hero(age=35, secret_name='Trevor Challa', id=5, name='Black Lion'),
Hero(age=36, secret_name='Steve Weird', id=6, name='Dr. Weird')
]
다음 묶음 조회하기
그다음 3개 행의 묶음을 가져오려면, 이미 봤던 처음 6개를 건너뛰면 돼요:
def select_heroes():
with Session(engine) as session:
statement = select(Hero).offset(6).limit(3)
results = session.exec(statement)
heroes = results.all()
print(heroes)
데이터베이스에는 지금 7개 행만 있어서, 이 쿼리는 행 1개만 가져올 수 있어요.
하지만 걱정 마세요. 행이 하나뿐인데 3개를 가져오려 해도 데이터베이스가 오류를 내진 않아요(파이썬 리스트였다면 그랬겠지만요).
데이터베이스는 우리가 결과 수를 제한하려 한다는 걸 아는데, 반드시 그만큼의 결과를 찾아야 하는 건 아니에요.
마지막 묶음으로 커맨드 라인에서 실행하기
커맨드 라인에서 실행하면 이렇게 출력돼요:
$ uv run python app.py
// 이전 출력은 생략했어요 🙈
// LIMIT와 OFFSET으로 마지막 묶음 SELECT
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
LIMIT ? OFFSET ?
INFO Engine [no key 0.00038s] (3, 6)
// 받은 히어로 묶음을 프린트, 단 한 명
[
Hero(age=93, secret_name='Esteban Rogelios', id=7, name='Captain North America')
]
SQL의 LIMIT와 OFFSET
아마 새 SQL 키워드 LIMIT와 OFFSET을 눈치채셨을 거예요.
SQL에서 다른 부분 뒤에 이렇게 쓸 수 있어요:
SELECT id, name, secret_name, age
FROM hero
LIMIT 3 OFFSET 6
DB Browser for SQLite에서 시도해 보면 같은 결과를 얻을 수 있어요.

LIMIT와 OFFSET을 WHERE와 조합하기
물론 .limit()과 .offset()을 .where()나 나중에 배울 다른 메서드들과 조합할 수도 있어요:
def select_heroes():
with Session(engine) as session:
statement = select(Hero).where(Hero.age > 32).offset(1).limit(2)
results = session.exec(statement)
heroes = results.all()
print(heroes)
LIMIT, OFFSET, WHERE를 다 써서 커맨드 라인에서 실행하기
커맨드 라인에서 실행하면, 데이터베이스에서 나이가 32보다 큰 히어로를 전부 찾아요. 보통 4명이겠죠.
하지만 우리는 offset 1 이후부터 포함하기 시작해서(첫 번째는 세지 않아요) 그 뒤 첫 2개만 가져오도록 제한했어요:
$ uv run python app.py
// 이전 출력은 생략했어요 🙈
// WHERE와 LIMIT, OFFSET으로 SELECT
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.age > ?
LIMIT ? OFFSET ?
INFO Engine [no key 0.00022s] (32, 2, 1)
// 받은 히어로를 프린트, 딱 2명
[
Hero(age=36, id=6, name='Dr. Weird', secret_name='Steve Weird'),
Hero(age=48, id=3, name='Rusty-Man', secret_name='Tommy Sharp')
]
요약
.where()나 다른 메서드로 데이터를 어떻게 필터링하든, .limit()으로 쿼리를 제한해 기껏해야 N개의 결과만 가져오게 할 수 있어요.
마찬가지로 .offset()으로 앞쪽 결과들을 건너뛸 수도 있어요.