SQLAlchemy:查询与操作 (三)

前两篇介绍了模型定义,这一篇介绍一下数据查询,这应该是日常开发中用得最多的部分。

一、基础查询

1. 查询所有

复制代码
# 方式一:query(传统写法)
users = session.query(User).all()

# 方式二:select(2.0 推荐写法)
from sqlalchemy import select
result = session.execute(select(User))
users = result.scalars().all()

两种写法对比:

写法 风格 推荐度
session.query(User) 1.x 传统 仍可用
select(User) 2.0 新风格 推荐

2. 查询单条

复制代码
# 按主键查
user = session.get(User, 1)  # 按主键 ID 查询

# 查第一条
user = session.query(User).first()

# 查唯一一条(不存在返回 None,多条报错)
user = session.query(User).filter_by(name="Alice").one_or_none()

3. 条件查询

复制代码
# filter_by:简单等值条件
users = session.query(User).filter_by(name="Alice").all()

# filter:支持复杂表达式
users = session.query(User).filter(User.age > 18).all()
users = session.query(User).filter(User.age >= 18, User.is_active == True).all()

filter_by vs filter:

方法 写法 说明
filter_by filter_by(name="Alice") 只支持等值,写法简单
filter filter(User.name == "Alice") 支持所有表达式

4. 常用查询条件

复制代码
from sqlalchemy import and_, or_, not_

# 等值
session.query(User).filter(User.name == "Alice")

# 不等
session.query(User).filter(User.age != 18)

# 大于/小于
session.query(User).filter(User.age > 18)
session.query(User).filter(User.age < 60)

# 范围
session.query(User).filter(User.age.between(18, 60))

# IN
session.query(User).filter(User.id.in_([1, 2, 3]))

# NOT IN
session.query(User).filter(~User.id.in_([1, 2, 3]))

# LIKE
session.query(User).filter(User.name.like("A%"))      # 以 A 开头
session.query(User).filter(User.name.like("%a%"))     # 包含 a

# IS NULL / IS NOT NULL
session.query(User).filter(User.email.is_(None))
session.query(User).filter(User.email.isnot(None))

# 逻辑组合
session.query(User).filter(and_(User.age > 18, User.is_active == True))
session.query(User).filter(or_(User.age < 18, User.age > 60))
session.query(User).filter(not_(User.is_active == True))

二、排序、分页、去重

1. 排序

复制代码
# 升序
session.query(User).order_by(User.age).all()

# 降序
session.query(User).order_by(User.age.desc()).all()

# 多字段排序
session.query(User).order_by(User.age.desc(), User.name.asc()).all()

2. 分页

复制代码
# 方式一:offset + limit
users = session.query(User).offset(20).limit(10).all()  # 第 21-30 条

# 方式二:切片
users = session.query(User).all()[20:30]

3. 去重

复制代码
# 去重
session.query(User.age).distinct().all()

# 统计不同年龄的数量
from sqlalchemy import distinct, func
count = session.query(func.count(distinct(User.age))).scalar()

三、聚合与分组

1. 常用聚合函数

复制代码
from sqlalchemy import func

# 总数
total = session.query(func.count(User.id)).scalar()

# 平均值
avg_age = session.query(func.avg(User.age)).scalar()

# 最大值/最小值
max_age = session.query(func.max(User.age)).scalar()
min_age = session.query(func.min(User.age)).scalar()

# 求和
total_amount = session.query(func.sum(Order.amount)).scalar()

2. 分组查询

复制代码
# 按年龄分组,统计每个年龄的人数
results = session.query(
    User.age,
    func.count(User.id).label("count")
).group_by(User.age).all()

for age, count in results:
    print(f"年龄 {age}: {count} 人")

3. 分组后过滤(HAVING)

复制代码
results = session.query(
    User.age,
    func.count(User.id).label("count")
).group_by(User.age).having(func.count(User.id) > 5).all()

四、关联查询

1. 通过关系属性访问(懒加载)

复制代码
# 查用户
user = session.query(User).first()

# 通过关系属性访问订单(此时才发 SQL)
for order in user.orders:
    print(order.amount)

问题:N+1 查询问题------查 1 个用户发 1 条 SQL,访问 orders 又发 N 条。

2. 预加载(解决 N+1)

复制代码
from sqlalchemy.orm import joinedload, selectinload

# joinedload:用 JOIN 一次性加载
users = session.query(User).options(joinedload(User.orders)).all()

# selectinload:用 IN 查询批量加载(推荐)
users = session.query(User).options(selectinload(User.orders)).all()

# 现在访问 user.orders 不会再发 SQL
for user in users:
    for order in user.orders:
        print(order.amount)

joinedload vs selectinload:

方法 原理 适用场景
joinedload LEFT JOIN 一对一、多对一
selectinload IN 查询 一对多、多对多(推荐)

3. 显式 JOIN

复制代码
# 查询用户及其订单金额
results = session.query(User.name, Order.amount)\
    .join(Order, User.id == Order.user_id)\
    .all()

for name, amount in results:
    print(f"{name}: {amount}")

4. 左连接

复制代码
# 查询所有用户,包括没有订单的
results = session.query(User.name, Order.amount)\
    .outerjoin(Order, User.id == Order.user_id)\
    .all()

5. JOIN 时带条件

复制代码
# 只查金额大于 100 的订单
results = session.query(User.name, Order.amount)\
    .join(Order, and_(User.id == Order.user_id, Order.amount > 100))\
    .all()

五、增删改操作

1. 插入

复制代码
# 单条插入
user = User(name="Alice", email="alice@example.com")
session.add(user)
session.commit()

# 批量插入
users = [User(name=f"user{i}") for i in range(100)]
session.add_all(users)
session.commit()

# 批量插入(性能更好)
session.bulk_save_objects(users)
session.commit()

2. 更新

复制代码
# 方式一:先查后改
user = session.query(User).filter_by(name="Alice").first()
user.age = 26
session.commit()

# 方式二:批量更新(不加载对象)
session.query(User).filter(User.age < 18).update({"is_active": False})
session.commit()

# 方式三:自增更新
session.query(User).filter(User.id == 1).update({"age": User.age + 1})
session.commit()

3. 删除

复制代码
# 方式一:删除对象
user = session.query(User).filter_by(name="Alice").first()
session.delete(user)
session.commit()

# 方式二:批量删除
session.query(User).filter(User.age < 18).delete()
session.commit()

4. 插入或更新(Upsert)

复制代码
from sqlalchemy.dialects.mysql import insert

stmt = insert(User).values(name="Alice", email="alice@example.com")
stmt = stmt.on_duplicate_key_update(age=stmt.inserted.age)
session.execute(stmt)
session.commit()

六、原生 SQL

有时候复杂查询用原生 SQL 更高效。

复制代码
from sqlalchemy import text

# 执行原生 SQL
result = session.execute(
    text("SELECT * FROM users WHERE age > :age"),
    {"age": 18}
)
for row in result:
    print(row.id, row.name)

# 返回字典
result = session.execute(text("SELECT * FROM users"))
for row in result.mappings():
    print(row["name"])
相关推荐
丨白色风车丨1 小时前
Python中JSON与字典/列表的相互转换详解
windows·python·json
计算机编程指导师1 小时前
【大数据毕设】基于Hadoop+Django乳腺癌数据分析与可视化系统选题推荐 源码 毕业设计 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·毕业设计·课程设计·乳腺癌
打工仔折腾 AI1 小时前
鸿蒙版ToDesk远控实测:蓝牙键鼠、双模式鼠标与跨端剪贴板怎么用
后端·python·华为·性能优化·计算机外设·harmonyos·ai agent 实战
Aric_Jones1 小时前
WAL / CDC / 复制槽 /位点
数据库·oracle
启观川1 小时前
数据结构与算法 -第 3 章 常用算法-回溯算法
数据结构·笔记·python·算法
草莓熊Lotso1 小时前
【C++ 基于微服务的即时通讯系统】基建三件套:gflags + gtest + spdlog 源码编译安装与实战入门
linux·网络·数据库·c++·微服务·log4j
计算机编程指导师1 小时前
Python毕设怎么做?基于hadoop的商家优惠券营销效果数据分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·spark·毕业设计·课程设计·优惠券
bksczm2 小时前
Redis基础篇之初识分布式
数据库·redis·分布式
计算机编程指导师2 小时前
【Python毕设推荐】基于Hadoop+Spark的商场商铺运行数据分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·spark·课程设计·商场商铺