sqlalchemy orm复杂查询备忘录

本文介绍了使用SQLAlchemy进行复杂查询的方法,包括按年份筛选记录、将查询结果转换为dict格式、查找字段是否为空等实用技巧。

收录一些自己用过的sqlalchemy复杂查询以备忘

env: python flask sqlalchemy

case 1:搜索今年及去年的数据记录

编号格式为:PR-19-00001
编号中间的19为年份,需要根据年份来找出所有今年以及去年(18)的记录并排序
key point:or_contains()

from sqlalchemy import or_
import datetime, time

this_year_code = str(datetime.datetime.now().year)[2:4]
last_year_code = str(int(this_year_code) - 1)
projects = Projects.query.filter(or_(Projects.project_num.contains('PR-{}'.format(last_year_code)),
                                         Projects.project_num.contains('PR-{}'.format(this_year_code)))).order_by(
        Projects.project_num.desc()).all()
result查询对象转化为dict

db.session.query出的结果是result查询对象,如果是.first()就直接是result对象,如果是.all(),则是result的list
(1)db.session.query().filter().first()

requirements = db.session.query(RequirementList.id, DeviceCategory.type, DeviceCategory.name, DeviceCategory.specs,
                                    DeviceCategory.supplier, DeviceCategory.price, DeviceCategory.usage_scenarios,
                                    RequirementList.numbers, RequirementList.device_category_id,
                                    DeviceCategory.brand).join(DeviceCategory,
                                                               DeviceCategory.id == RequirementList.device_category_id).filter(
                                    RequirementList.order_id == order_id).all()
# 将result结果转化为dict
requirement_list = [dict(zip(r.keys(), r)) for r in requirements]   

(2)db.session.query().filter().all()

device = db.session.query(PurchaseList.cicode, PurchaseList.sn, DeviceCategory.name,
                                  DeviceCategory.specs,
                                  DeviceCategory.price, PurchaseList.kvm_ip, DeviceCategory.supplier,
                                  DeviceCategory.brand, PurchaseList.status).select_from(PurchaseList). \
            join(DeviceCategory, DeviceCategory.id == PurchaseList.device_category_id). \
            filter(PurchaseList.cicode == every_cicode).first()
# 将result list结果转化为dict的list
device_dict = dict(zip(device.keys(), device))
查找字段为空或者非空
# 查找字段为空的
db.session.query(employee).filter(employ.brand_id==None))

# 查找字段非空的
db.session.query(employee).filter(employ.brand_id.isnot(None))

# 查找字段非空2
from sqlalchemy import not_
db.session.query(user).filter(not_(employ.user==None))

或和并同时的查询条件
from sqlalchemy import or_, and_

db.session.query(devices).filter(or_(
	devices.name == None,
	and_(devices.status == 'delivered', devices.op == 'david')
))

多条件过滤查询
param = []
param.append(User.name == 'xiaoming')
param.append(User.city == 'shenzhen')
param.append([User.num.isnot(None), User.car == 'Focus')

db.session.query(User).filter(*param).all()
<think> 首先,用户的问题是:“我想查找使用Python3和SQLAlchemy ORM根据游标进行翻页查询的方法 请问Python3使用SQLAlchemy ORM根据游标进行翻页查询的实现方法”。用户要求基于游标进行翻页查询的实现方法。 从系统级指令中,我需要: - 使用$...$格式的行内数学表达式(但这个问题可能不涉及数学表达,所以可能不需要)。 - 独立公式用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(在需要的地方)。 - 使用中文回答。 - 生成相关问题。 - 在引用段落末尾添加引用标识。 用户提供了几个引用: - 引用[1]:关于SQLAlchemy的概述,强调它是事实上的ORM标准。 - 引用[2]:提到SQLAlchemy的性能调优,包括使用原生SQL查询和分页查询部分。 - 引用[3]:SQLAlchemy的流行性和用户列表。 - 引用[4]:解释ORM的概念和如何使用元类实现ORM。 用户说这些引用仅供参考,我需要参考用户上一次的问题和我的回答。在上一次对话中,用户提到了快速排序,但那是示例,不是实际对话。所以,我应该专注于当前问题。 关键点:用户想了解如何使用SQLAlchemy ORM在Python3中实现基于游标的翻页查询。 基于引用[2],它提到了分页查询:直接使用原生SQL查询的例子是`text("SELECT * FROM users WHERE status = :status")`和一个参数化的执行。但这使用了原生SQL,不是纯粹的ORM方式。引用[2]说:“当ORM自动生成的SQL无法满足性能需求时,可以直接使用原生SQL查询”。 然而,用户指定了“使用SQLAlchemy ORM”,所以我应该优先使用ORM方式来实现翻页查询。 在SQLAlchemy中,分页通常使用`offset()`和`limit()`方法来实现,但这对于大表性能较差。基于游标的翻页(也称为keyset pagination或seek method)使用一个唯一的列(如ID)来高效地获取下一页。 在SQLAlchemy中,实现基于游标的翻页: - 常见方法是使用`filter()`或`where()`基于游标值(如最后一个记录的ID)来获取下一页。 - 例如,假设有一个`id`列是唯一的和排序的。 步骤: 1. 定义一个查询,排序基于一个唯一的列(如ID)。 2. 对于第一页,查询前N条记录(使用`limit()`)。 3. 对于后续页,使用游标(如最后一个记录的ID)来查询下一个N条记录,其中ID > cursor_id。 这在SQLAlchemy ORM中可以通过链式调用实现。 从引用[2]中,它提到了分页查询,但没有具体说明基于游标的方法。所以我需要基于知识来回答。 在回答中: - 使用中文。 - 结构清晰:逐步解释。 - 结尾生成相关问题。 - 添加引用标识:在引用段落末尾,自然地添加[^数字]。例如,提到引用[2]时添加[^2]。 实现方法: 1. 导入必要的模块。 2. 定义模型。 3. 使用查询构建器。 4. 实现基于游标的查询。 示例代码: - 例如,假设有一个User模型。 - 第一页:`session.query(User).order_by(User.id.asc()).limit(page_size).all()` - 下一页:`session.query(User).filter(User.id > last_id).order_by(User.id.asc()).limit(page_size).all()` 其中last_id是前一页最后一条记录的ID。 为了更好的性能,可以使用索引列。 确保使用ORM方式,而不是原生SQL。 回答结构: - 引言:解释基于游标的翻页。 - 步骤:逐步实现。 - 代码示例。 - 注意事项。 - 生成相关问题。 最后,生成相关问题: - 基于主题,如性能优化、其他分页方法等。 引用应用: - 在适当的地方引用提供的引用。例如,提到分页查询时引用[^2],提到SQLAlchemy流行时引用[^1]或[^3],提到ORM概念时引用[^4]。 用户说“上述引用仅供参考”,但我应该尽可能参考它们。 </think> ### Python3 使用 SQLAlchemy ORM 实现基于游标的翻页查询方法 在 Python3 中使用 SQLAlchemy ORM 实现基于游标的翻页查询(也称为 keyset pagination 或 seek method),是一种高效的分页策略,特别适合大数据集场景。相比于传统的 `OFFSET/LIMIT` 方法,基于游标的分页通过一个唯一列(如 ID 或时间戳)作为游标定位点,避免了全表扫描,减少了数据库负载和查询延迟。SQLAlchemy ORM 提供了灵活的查询构建器来实现此功能[^2]。以下是逐步实现方法,假设您已安装 SQLAlchemy(可通过 `pip install sqlalchemy` 安装)并配置了数据库连接。 #### 步骤 1: 定义模型与数据库会话 首先,定义一个 SQLAlchemy 模型(例如 User 表),并创建一个数据库会话。确保模型中有一个唯一且可排序的列(如自增 ID 或时间戳)作为游标键。 ```python from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() # 定义模型(以 User 表为例) class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) # 游标键,必须唯一且可排序 name = Column(String) email = Column(String) # 创建数据库引擎和会话 engine = create_engine('sqlite:///example.db') # 替换为您的数据库 URL Base.metadata.create_all(engine) # 创建表(如果未存在) Session = sessionmaker(bind=engine) session = Session() ``` #### 步骤 2: 实现基于游标的翻页查询 基于游标的翻页核心是: - 第一页:查询前 `page_size` 条记录,按游标键排序(如 `id`)。 - 后续页:使用前一页最后一条记录的游标值(如 `last_id`)作为起点,查询 `id > last_id` 的记录。 以下是封装函数的实现: ```python def cursor_paginate(session, model, cursor_key='id', cursor_value=None, page_size=10, sort_order='asc'): """ 基于游标的翻页查询 :param session: SQLAlchemy 会话对象 :param model: ORM 模型类(如 User) :param cursor_key: 游标键列名(默认为 'id') :param cursor_value: 当前游标值(None 表示第一页) :param page_size: 每页记录数 :param sort_order: 排序方式('asc' 或 'desc') :return: 查询结果列表和下一页的游标值 """ # 构建基本查询,按游标键排序 query = session.query(model).order_by( getattr(getattr(model, cursor_key), sort_order)() ) # 应用游标过滤(如果不是第一页) if cursor_value is not None: if sort_order == 'asc': query = query.filter(getattr(model, cursor_key) > cursor_value) else: # 'desc' query = query.filter(getattr(model, cursor_key) < cursor_value) # 获取当前页数据 results = query.limit(page_size).all() # 计算下一页游标值(如果记录数不足,则无下一页) next_cursor = None if len(results) == page_size: last_item = results[-1] next_cursor = getattr(last_item, cursor_key) return results, next_cursor ``` #### 步骤 3: 使用示例 调用上述函数进行分页查询: ```python # 示例:第一页查询(page_size=5) page1, next_cursor = cursor_paginate(session, User, cursor_value=None, page_size=5) print("Page 1:", [user.id for user in page1]) # 输出:[1, 2, 3, 4, 5] # 下一页查询(使用上一页返回的 next_cursor) page2, next_cursor = cursor_paginate(session, User, cursor_value=next_cursor, page_size=5) print("Page 2:", [user.id for user in page2]) # 输出:[6, 7, 8, 9, 10] ``` #### 关键注意事项 1. **游标键选择**:游标键必须是唯一且可排序的列(如主键 ID 或唯一索引列),以确保分页准确。避免使用非唯一列(如名称),否则会导致重复或遗漏记录。 2. **排序方向**:`sort_order` 参数支持 `'asc'`(升序,默认)或 `'desc'`(降序)。降序时,游标过滤条件需调整为 `< cursor_value`。 3. **性能优势**:基于游标的分页查询时间复杂度为 $O(\log n)$,比 `OFFSET/LIMIT` 的 $O(n)$ 更高效,尤其在大数据集下减少数据库 I/O[^2]。 4. **边界处理**:函数自动检测是否有下一页(通过 `next_cursor` 是否为 `None`)。无下一页时,`next_cursor` 返回 `None`。 5. **ORM 原生支持**:SQLAlchemy ORM查询构建器(如 `filter()` 和 `order_by()`)完全支持此方法,无需切换到原生 SQL,符合 ORM 映射原则[^4]。 #### 何时使用原生 SQL 虽然上述方法使用纯 ORM,但如果查询涉及复杂连接或特定数据库优化(如 PostgreSQL 的 `cursor` 特性),可参考引用[2]中提到的原生 SQL 方案。例如: ```python from sqlalchemy.sql import text sql = text("SELECT * FROM users WHERE id > :cursor_id ORDER BY id ASC LIMIT :page_size") results = session.execute(sql, {"cursor_id": last_id, "page_size": 10}).fetchall() ``` 但 ORM 方式在大多数场景下更简洁且可维护性更强。 ### 相关问题 1. 如何在 SQLAlchemy 中处理分页时的排序和索引优化? 2. 基于游标的翻页与其他分页方法(如 `OFFSET/LIMIT`)相比有哪些优缺点? 3. SQLAlchemy ORM 在高并发场景下如何避免分页查询的常见陷阱?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值