Метод yield_per
Метод yield_per класса Result возвращает итератор,
который выдаёт строки результата пакетами заданного размера.
Это позволяет обрабатывать большие выборки без загрузки всех
строк в память сразу. Первым параметром передаётся целое число
num_rows - размер пакета. Второй необязательный параметр
max_row_buffer задаёт максимальный размер буфера.
Метод удобен при чтении миллионов записей: драйвер базы данных
отдаёт строки порциями, а SQLAlchemy не кеширует их целиком.
Для ORM-запросов метод также доступен через объект Result,
полученный из Session.execute.
Синтаксис
Result.yield_per(num_rows, [max_row_buffer])
Пример
Давайте выполним простой запрос и получим строки пакетами по две штуки:
from sqlalchemy import create_engine, text
engine = create_engine('sqlite:///:memory:')
with engine.connect() as conn:
conn.execute(text('CREATE TABLE articles (id INTEGER, title TEXT)'))
conn.execute(text("INSERT INTO articles (id, title) VALUES (1, 'article 1')"))
conn.execute(text("INSERT INTO articles (id, title) VALUES (2, 'article 2')"))
conn.execute(text("INSERT INTO articles (id, title) VALUES (3, 'article 3')"))
res = conn.execute(text('SELECT id, title FROM articles ORDER BY id'))
for row in res.yield_per(2):
print(row)
Результат выполнения кода:
(1, 'article 1')
(2, 'article 2')
(3, 'article 3')
Пример
Давайте используем yield_per вместе с ORM-моделью
и выведем заголовки статей:
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(50))
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session
from models import Base, Article
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(id=1, title='article 1'),
Article(id=2, title='article 2'),
Article(id=3, title='article 3'),
])
session.commit()
res = session.execute(select(Article).order_by(Article.id))
for article in res.scalars().yield_per(2):
print(article.title)
Результат выполнения кода:
"article 1"
"article 2"
"article 3"
Пример
Давайте убедимся, что yield_per действительно отдаёт
строки по одной, а не списком:
from sqlalchemy import create_engine, text
engine = create_engine('sqlite:///:memory:')
with engine.connect() as conn:
conn.execute(text('CREATE TABLE articles (id INTEGER, title TEXT)'))
conn.execute(text("INSERT INTO articles (id, title) VALUES (1, 'article')"))
conn.execute(text("INSERT INTO articles (id, title) VALUES (2, 'user')"))
res = conn.execute(text('SELECT id, title FROM articles'))
iterator = res.yield_per(1)
print(type(iterator).__name__)
print(next(iterator))
print(next(iterator))
Результат выполнения кода:
"ChunkedIteratorResult"
(1, 'article')
(2, 'user')
Смотрите также
-
класс
Result,
который представляет результат выполнения запроса -
метод
partitions,
который разбивает результат на части -
метод
scalars,
который возвращает итератор по первым столбцам -
метод
all,
который возвращает все строки списком