SqlAlchemy 2.0 中文文档(七十九)(3)

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: SqlAlchemy 2.0 中文文档(七十九)

SqlAlchemy 2.0 中文文档(七十九)(2)https://developer.aliyun.com/article/1561103


速度增强

与所有主要 SQLA 版本一样,通过内部进行广泛的遍历以减少开销和调用次数,进一步减少了常见情况下所需的工作量。此版本的亮点包括:

  • 刷新过程现在将 INSERT 语句捆绑成批次提供给cursor.executemany(),对于主键已经存在的行。特别是这通常适用于连接表继承配置中的“子”表,这意味着对于大量连接表对象的批量插入,可以将cursor.execute的调用次数减少一半,从而允许针对那些传递给cursor.executemany()的语句进行本地 DBAPI 优化(例如重用准备好的语句)。
  • 当访问已加载的相关对象的多对一引用时,调用的代码路径已经大大简化。直接检查标识映射,无需首先生成一个新的Query对象,这在访问成千上万个内存中的多对一时是昂贵的。对于大多数延迟属性加载,也不再使用每次构造的“加载器”对象。
  • 重写复合体允许在映射器内部访问在刷新中与映射属性相关的属性时,使用更短的代码路径。
  • 新的内联属性访问函数取代了以前在“save-update”和其他级联操作需要在属性的所有数据成员范围内级联时使用“history”的用法。这减少了为这个速度关键操作生成新的History对象的开销。
  • ExecutionContext的内部,对应于语句执行的对象,已经内联并简化。
  • 为每个语句执行生成的类型的bind_processor()result_processor()可调用现在被缓存(小心翼翼地,以避免对临时类型和方言造成内存泄漏),在该类型的生命周期内,进一步减少每个语句调用的开销。
  • 特定Compiled实例的“绑定处理器”集合也被缓存在Compiled对象上,进一步利用刷新过程使用的“编译缓存”,以重用相同的 INSERT、UPDATE、DELETE 语句的编译形式。

一个减少调用次数的演示,包括一个示例基准脚本,位于techspot.zzzeek.org/2010/12/12/a-tale-of-three- profiles/。

复合体重写

“复合”功能已经被重写,就像synonym()comparable_property()一样,使用基于描述符和事件的轻量级实现,而不是构建到 ORM 内部。这允许从映射器/工作单元内部删除一些延迟,并简化复合的工作方式。复合属性现在不再隐藏其构建在其上的基础列,这些列现在保持为常规属性。复合还可以充当relationship()以及Column()属性的代理。

复合的主要不兼容变更是,它们不再使用mutable=True系统来检测原地变异。请使用Mutation Tracking扩展来建立对现有复合使用的原地更改事件。

另请参见

复合列类型

变异跟踪

#2008 #2024

更简洁的查询.join(target, onclause)形式

向具有显式 onclause 的目标发出query.join()的默认方法现在是:

query.join(SomeClass, SomeClass.id == ParentClass.some_id)

在 0.6 版本中,这种用法被认为是错误的,因为join()接受多个参数对应于多个 JOIN 子句 -  两个参数形式需要在元组中以消除单参数和双参数连接目标之间的歧义。在 0.6  的中间,我们添加了检测和针对这种特定调用风格的错误消息,因为这种情况非常普遍。在 0.7  中,由于我们无论如何都在检测确切的模式,并且由于不得不无缘无故地输入元组非常恼人,非元组方法现在成为“正常”做法。与单个连接情况相比,“多个  JOIN”用例极为罕见,而如今多个连接更清晰地表示为多次调用join()

元组形式将保留以确保向后兼容性。

请注意,所有其他形式的query.join()保持不变:

query.join(MyClass.somerelation)
query.join("somerelation")
query.join(MyTarget)
# ... etc

使用连接查询

#1923

变异事件扩展,取代“mutable=True”

一个新的扩展,变异跟踪,提供了一种机制,通过该机制,用户定义的数据类型可以向拥有的父级或父级提供更改事件。该扩展包括一种用于标量数据库值的方法,例如由PickleType管理的值,postgresql.ARRAY或其他自定义MutableType类,以及一种用于 ORM“复合”配置的方法,这些配置使用composite()

另请参见

变异跟踪

NULLS FIRST / NULLS LAST 操作符

这些被实现为asc()desc()运算符的扩展,称为nullsfirst()nullslast()

另请参阅

nullsfirst()

nullslast()

#723

select.distinct(),query.distinct()接受*args 用于 PostgreSQL DISTINCT ON

通过将表达式列表传递给select()distinct关键字参数,现在select()Querydistinct()方法接受位置参数,当使用 PostgreSQL 后端时,这些参数将被渲染为 DISTINCT ON。

distinct()

Query.distinct()

#1069

Index()可以内联放置在Table__table_args__

Index()构造可以与 Table 定义内联创建,使用字符串作为列名,作为在 Table 之外创建索引的替代方法。即:

Table(
    "mytable",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(50), nullable=False),
    Index("idx_name", "name"),
)

这里的主要原因是为了声明性__table_args__的好处,特别是在与混合使用时:

class HasNameMixin(object):
    name = Column("name", String(50), nullable=False)
    @declared_attr
    def __table_args__(cls):
        return (Index("name"), {})
class User(HasNameMixin, Base):
    __tablename__ = "user"
    id = Column("id", Integer, primary_key=True)

Indexes

窗口函数 SQL 构造

“窗口函数”为语句提供了有关生成的结果集的信息。这允许根据诸如“行号”、“排名”等各种条件进行查询。它们至少被已知支持的 PostgreSQL、SQL Server 和 Oracle 支持,可能还有其他数据库。

关于窗口函数的最佳介绍在 PostgreSQL 的网站上,窗口函数自 8.4 版本起就得到支持:

www.postgresql.org/docs/current/static/tutorial-window.html

SQLAlchemy 提供了一个简单的构造,通常通过现有的函数子句调用,使用over()方法,接受order_bypartition_by关键字参数。下面我们复制了 PG 教程中的第一个示例:

from sqlalchemy.sql import table, column, select, func
empsalary = table("empsalary", column("depname"), column("empno"), column("salary"))
s = select(
    [
        empsalary,
        func.avg(empsalary.c.salary)
        .over(partition_by=empsalary.c.depname)
        .label("avg"),
    ]
)
print(s)

SQL:

SELECT  empsalary.depname,  empsalary.empno,  empsalary.salary,
avg(empsalary.salary)  OVER  (PARTITION  BY  empsalary.depname)  AS  avg
FROM  empsalary

sqlalchemy.sql.expression.over

#1844

Connection 上的 execution_options()接受“isolation_level”参数

这为单个Connection设置了事务隔离级别,直到该Connection关闭并其底层 DBAPI 资源返回到连接池,此时隔离级别将重置为默认值。默认的隔离级别是使用create_engine()isolation_level参数设置的。

事务隔离支持目前仅由 PostgreSQL 和 SQLite 后端支持。

execution_options()

#2001

TypeDecorator 与整数主键列一起使用

可以使用扩展Integer行为的TypeDecorator与主键列一起使用。Column的“autoincrement”特性现在将识别到底层数据库列仍然是整数,以便lastrowid机制继续正常工作。TypeDecorator本身的结果值处理器将应用于新生成的主键,包括通过 DBAPI cursor.lastrowid访问器接收到的主键。

#2005 #2006

TypeDecorator 存在于“sqlalchemy”导入空间中

不再需要从sqlalchemy.types导入,现在在sqlalchemy中有镜像。

新方言

已添加方言:

  • 用于 Drizzle 数据库的 MySQLdb 驱动程序:
    Drizzle
  • 支持 pymysql DBAPI:
    pymsql Notes
  • psycopg2 现在与 Python 3 兼容

行为变更(向后兼容)

默认情况下构建 C 扩展

这是从 0.7b4 开始的。如果检测到 cPython 2.xx,则会构建扩展。如果构建失败,例如在 Windows 安装中,会捕获该条件并继续非 C 安装。如果使用 Python 3 或 PyPy,则不会构建 C 扩展。

简化的 Query.count(),几乎总是有效

Query.count()内部的非常古老的猜测现在已经被现代化,使用.from_self()。也就是说,query.count()现在等效于:

query.from_self(func.count(literal_column("1"))).scalar()

以前,内部逻辑尝试重写查询本身的列子句,并在检测到“子查询”条件时,例如可能在其中具有聚合的基于列的查询,或具有 DISTINCT 的查询时,会经历一个繁琐的过程来重写列子句。这种逻辑在复杂条件下失败,特别是涉及联接表继承的条件,并且长期以来已经被更全面的.from_self()调用所淘汰。

query.count()生成的 SQL 现在总是形式为:

SELECT  count(1)  AS  count_1  FROM  (
  SELECT  user.id  AS  user_id,  user.name  AS  user_name  from  user
)  AS  anon_1

换句话说,原始查询完全保留在子查询中,不再需要猜测如何应用计数。

#2093

发出非子查询形式的 count()

MySQL 用户已经报告说,MyISAM 引擎在这个简单的更改中完全崩溃,这并不奇怪。请注意,对于优化不能处理简单子查询的数据库的简单count(),应该使用func.count()

from sqlalchemy import func
session.query(func.count(MyClass.id)).scalar()

或者对于count(*)

from sqlalchemy import func, literal_column
session.query(func.count(literal_column("*"))).select_from(MyClass).scalar()

LIMIT/OFFSET 子句现在使用绑定参数

LIMIT 和 OFFSET 子句,或其后端等效项(即 TOP,ROW NUMBER OVER  等),对于支持它的所有后端使用绑定参数进行实际值,(除了 Sybase 之外的大多数后端)。这样做可以提高查询优化器的性能,因为具有不同  LIMIT/OFFSET 的多个语句的文本字符串现在是相同的。

#805

日志增强

Vinay Sajip 提供了一个补丁,使我们的日志系统中不再需要在引擎和池的日志语句中嵌入“十六进制字符串”以使echo标志正常工作。使用过滤日志对象的新系统使我们能够保持echo仅适用于各个引擎而无需额外的标识字符串。

#1926

简化的多态 _on 赋值

在继承场景中使用时,polymorphic_on列映射属性的填充现在发生在对象构造时,即调用其__init__方法时,使用  init  事件。然后,该属性的行为与任何其他列映射属性相同。以前,特殊逻辑会在刷新期间触发以填充此列,这会阻止任何用户代码修改其行为。新方法在三个方面改进了这一点:1.多态标识现在在对象构造时立即存在;2.用户代码可以更改多态标识而不会与任何其他列映射属性有任何不同的行为;3.在刷新期间,映射器的内部简化,不再需要对此列进行特殊检查。

#1895

跨多个路径(即“all()”)的 contains_eager()链

contains_eager()修改器现在会把自己链接到一个更长的路径上,而不需要释放独立的contains_eager()调用。而不是:

session.query(A).options(contains_eager(A.b), contains_eager(A.b, B.c))

你可以说:

session.query(A).options(contains_eager(A.b, B.c))

#2032

允许刷新没有父级的孤儿

我们一直有一个长期存在的行为,即在刷新时检查所谓的“孤儿”,即与指定“delete-orphan”级联的relationship()相关联的对象,已经被新添加到会话中进行  INSERT 操作,但尚未建立父关系。多年前添加了此检查以适应一些测试用例,这些测试用例测试了孤儿行为的一致性。在现代 SQLA  中,这种检查在 Python 端不再需要。通过使对象的外键引用对象的父行为 NOT NULL,数据库会以 SQLA  允许大多数其他操作执行的方式确保数据一致性,从而实现“孤儿检查”的等效行为。如果对象的父外键是可为空的,则可以插入行。当对象与特定父对象一起持久化,然后与该父对象解除关联时,会触发“孤儿”行为,导致为其发出  DELETE 语句。

#1912

在收集成员,不是刷新的标量引用时生成的警告

当通过父对象上标记为“脏”的加载relationship()引用的相关对象在当前Session中不存在时,现在会发出警告。

当对象被添加到Session时,或者当对象首次与父对象关联时,save-update级联生效,以便对象及其所有相关内容通常都存在于同一个Session中。但是,如果对于特定的relationship()禁用了save-update级联,则此行为不会发生,并且刷新过程不会尝试纠正它,而是保持一致到配置的级联行为。以前,在刷新期间检测到这样的对象时,它们会被静默跳过。新行为是发出警告,目的是提醒一个经常是意外行为来源的情况。

#1973

设置不再安装 Nose 插件

自从我们转向 nose 以来,我们使用了一个通过 setuptools 安装的插件,这样nosetests脚本会自动运行  SQLA 的插件代码,这对于我们的测试来说是必要的,以便有一个完整的环境。在 0.6 的中间,我们意识到这里的导入模式意味着 Nose  的“coverage”插件会中断,因为“coverage”要求在导入要覆盖的任何模块之前启动它;所以在 0.6 的中间,我们通过添加一个单独的sqlalchemy-nose包来克服这一情况,使情况变得更糟。

在 0.7 中,我们已经放弃了尝试让nosetests自动工作,因为 SQLAlchemy 模块会为所有nosetests的用法产生大量的 nose 配置选项,而不仅仅是 SQLAlchemy 单元测试本身,而且额外的sqlalchemy-nose安装甚至更糟,会在 Python 环境中产生一个额外的包。在 0.7 中,sqla_nose.py脚本现在是使用 nose 运行测试的唯一方法。

#1949

Table派生的构造可以被映射

一个根本不针对任何Table的构造,比如一个函数,可以被映射。

from sqlalchemy import select, func
from sqlalchemy.orm import mapper
class Subset(object):
    pass
selectable = select(["x", "y", "z"]).select_from(func.some_db_function()).alias()
mapper(Subset, selectable, primary_key=[selectable.c.x])

#1876

aliased()接受FromClause元素

这是一个方便的辅助工具,以便在传递一个普通的FromClause,比如一个selectTablejoinorm.aliased()构造时,它会通过到该 from 构造的.alias()方法,而不是构造一个 ORM 级别的AliasedClass

#2018

Session.connection(),Session.execute()接受‘bind’

这是为了允许执行/连接操作明确参与引擎的打开事务。它还允许Session的自定义子类实现自己的get_bind()方法和参数,以便在execute()connection()方法中等效地使用这些自定义参数。

Session.connection Session.execute

#1996

列子句中的独立绑定参数自动标记。

在选择的“列子句”中存在的绑定参数现在像其他“匿名”子句一样自动标记,这样在获取行时它们的“类型”就有意义,就像结果行处理器一样。

SQLite - 相对文件路径通过 os.path.abspath()进行标准化

这样,更改当前目录的脚本将继续以后续建立的 SQLite 连接的相同位置为目标。

#2036

MS-SQL - String/Unicode/VARCHAR/NVARCHAR/VARBINARY在未指定长度时发出“max”

在 MS-SQL 后端,String/Unicode 类型及其对应的 VARCHAR/NVARCHAR 类型,以及 VARBINARY (#1833)在未指定长度时发出“max”作为长度。这使其与 PostgreSQL 的 VARCHAR 类型更兼容,当未指定长度时同样是无界的。SQL Server 在未指定长度时将这些类型的长度默认为‘1’。

默认构建 C 扩展

这是从 0.7b4 开始的。如果检测到 cPython 2.xx,则将构建扩展。如果构建失败,例如在 Windows 安装中,将捕获该条件并继续非 C 安装。如果使用 Python 3 或 PyPy,则不会构建 C 扩展。

Query.count()简化,几乎总是有效

Query.count()内部的非常古老的猜测现已现代化为使用.from_self()。也就是说,query.count()现在等效于:

query.from_self(func.count(literal_column("1"))).scalar()

以前,内部逻辑尝试重写查询本身的列子句,并在检测到“子查询”条件时,例如可能在其中具有聚合函数的基于列的查询,或具有 DISTINCT 的查询,将经历一个复杂的过程来重写列子句。这种逻辑在复杂条件下失败,特别是涉及联接表继承的条件,并且已经被更全面的.from_self()调用长时间废弃。

query.count()发出的 SQL 现在始终是以下形式:

SELECT  count(1)  AS  count_1  FROM  (
  SELECT  user.id  AS  user_id,  user.name  AS  user_name  from  user
)  AS  anon_1

即原始查询完全保留在子查询中,不再猜测应如何应用计数。

#2093

发出非子查询形式的 count()

MySQL 用户已经报告说,MyISAM 引擎不出所料地完全崩溃了这个简单的更改。请注意,对于优化无法处理简单子查询的数据库的简单count(),应使用func.count()

from sqlalchemy import func
session.query(func.count(MyClass.id)).scalar()

或对于count(*)

from sqlalchemy import func, literal_column
session.query(func.count(literal_column("*"))).select_from(MyClass).scalar()
发出非子查询形式的 count()

MySQL 用户已经报告说,MyISAM 引擎不出所料地完全崩溃了这个简单的更改。请注意,对于优化无法处理简单子查询的数据库的简单count(),应使用func.count()

from sqlalchemy import func
session.query(func.count(MyClass.id)).scalar()

或对于count(*)

from sqlalchemy import func, literal_column
session.query(func.count(literal_column("*"))).select_from(MyClass).scalar()

LIMIT/OFFSET 子句现在使用绑定参数

LIMIT 和 OFFSET 子句,或其后端等效项(即 TOP,ROW NUMBER OVER  等),对实际值使用绑定参数,对于支持它的所有后端(除了 Sybase)。这允许更好的查询优化器性能,因为具有不同 LIMIT/OFFSET  的多个语句的文本字符串现在是相同的。

#805


SqlAlchemy 2.0 中文文档(七十九)(4)https://developer.aliyun.com/article/1561107

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(六十四)(1)
SqlAlchemy 2.0 中文文档(六十四)
10 0
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十九)(5)
SqlAlchemy 2.0 中文文档(七十九)
9 2
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(1)
SqlAlchemy 2.0 中文文档(七十六)
11 2
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(2)
SqlAlchemy 2.0 中文文档(七十六)
17 2
|
3天前
|
关系型数据库 测试技术 API
SqlAlchemy 2.0 中文文档(二十八)(3)
SqlAlchemy 2.0 中文文档(二十八)
9 1
|
3天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(二十八)(4)
SqlAlchemy 2.0 中文文档(二十八)
12 1
|
3天前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(八十一)(1)
SqlAlchemy 2.0 中文文档(八十一)
10 2
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(八十一)(2)
SqlAlchemy 2.0 中文文档(八十一)
13 1
|
3天前
|
存储 SQL Java
SqlAlchemy 2.0 中文文档(八十一)(4)
SqlAlchemy 2.0 中文文档(八十一)
16 1
|
3天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(八十一)(3)
SqlAlchemy 2.0 中文文档(八十一)
13 1