Метод having класса Query
Метод having класса Query добавляет
условие HAVING к SQL-запросу. Он применяется
после группировки group_by и позволяет
отфильтровать уже сгруппированные строки по
агрегатным функциям, таким как count,
sum, avg, max, min.
Первым параметром метод принимает условие фильтрации,
которое может быть выражением SQLAlchemy или
текстовой строкой.
Синтаксис
query.having(criterion)
Пример
Давайте создадим модель Article и несколько
записей для демонстрации работы метода:
from sqlalchemy import create_engine, String, Integer
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))
text: Mapped[str] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(20))
num: Mapped[int] = mapped_column(Integer)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(title='article 1', text='text 1', status='draft', num=1),
Article(title='article 2', text='text 2', status='draft', num=2),
Article(title='article 3', text='text 3', status='published', num=3),
Article(title='article 4', text='text 4', status='published', num=4),
Article(title='article 5', text='text 5', status='published', num=5),
])
session.commit()
Пример
Давайте сгруппируем статьи по статусу и оставим только те группы, в которых больше двух статей:
from sqlalchemy import create_engine, String, Integer, func
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))
text: Mapped[str] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(20))
num: Mapped[int] = mapped_column(Integer)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(title='article 1', text='text 1', status='draft', num=1),
Article(title='article 2', text='text 2', status='draft', num=2),
Article(title='article 3', text='text 3', status='published', num=3),
Article(title='article 4', text='text 4', status='published', num=4),
Article(title='article 5', text='text 5', status='published', num=5),
])
session.commit()
res = session.query(
Article.status,
func.count(Article.id).label('cnt')
).group_by(Article.status).having(func.count(Article.id) > 2).all()
print(res)
Результат выполнения кода:
[('published', 3)]
Пример
Давайте отфильтруем группы по максимальному значению
поля num, оставив только те, где максимум
больше трёх:
from sqlalchemy import create_engine, String, Integer, func
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))
text: Mapped[str] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(20))
num: Mapped[int] = mapped_column(Integer)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(title='article 1', text='text 1', status='draft', num=1),
Article(title='article 2', text='text 2', status='draft', num=2),
Article(title='article 3', text='text 3', status='published', num=3),
Article(title='article 4', text='text 4', status='published', num=4),
Article(title='article 5', text='text 5', status='published', num=5),
])
session.commit()
res = session.query(
Article.status,
func.max(Article.num).label('max_num')
).group_by(Article.status).having(func.max(Article.num) > 3).all()
print(res)
Результат выполнения кода:
[('published', 5)]
Пример
Давайте посмотрим итоговый SQL-запрос, который
генерирует метод having:
from sqlalchemy import create_engine, String, Integer, func
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))
text: Mapped[str] = mapped_column(String(200))
status: Mapped[str] = mapped_column(String(20))
num: Mapped[int] = mapped_column(Integer)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
query = session.query(
Article.status,
func.count(Article.id).label('cnt')
).group_by(Article.status).having(func.count(Article.id) > 2)
print(query)
Итоговый SQL запрос:
SELECT articles.status AS articles_status, count(articles.id) AS cnt
FROM articles
GROUP BY articles.status
HAVING count(articles.id) > ?