SQLAlchemy 2.0 文档
方言
- PostgreSQL
- MySQL 和 MariaDB
- SQLite
- Oracle¶
- 对 Oracle 数据库数据库的支持。
- 自动递增行为
- 事务隔离级别 / 自动提交
- 标识符大小写
- 最大标识符长度
- LIMIT/OFFSET/FETCH 支持
- RETURNING 支持
- ON UPDATE CASCADE
- Oracle 数据库 8 兼容性
- 同义词/DBLINK 反射
- 约束反射
- 具有 SYSTEM/SYSAUX 表空间的表名
- FLOAT / DOUBLE 支持和行为
- DateTime 兼容性
- Oracle 数据库表选项
- Oracle 数据库特定索引选项
- Oracle 数据库数据类型
- python-oracledb
- cx_Oracle
- Microsoft SQL Server
项目版本
- 上一篇: SQLite
- 下一篇: Microsoft SQL Server
- 上一级: 首页
- 在本页
- Oracle
- 对 Oracle 数据库数据库的支持。
- 自动递增行为
- 事务隔离级别 / 自动提交
- 标识符大小写
- 最大标识符长度
- LIMIT/OFFSET/FETCH 支持
- RETURNING 支持
- ON UPDATE CASCADE
- Oracle 数据库 8 兼容性
- 同义词/DBLINK 反射
- 约束反射
- 具有 SYSTEM/SYSAUX 表空间的表名
- FLOAT / DOUBLE 支持和行为
- DateTime 兼容性
- Oracle 数据库表选项
- Oracle 数据库特定索引选项
- Oracle 数据库数据类型
- python-oracledb
- cx_Oracle
Oracle¶
对 Oracle 数据库数据库的支持。
下表总结了当前数据库发布版本的支持级别。
支持类型 |
版本 |
---|---|
11+ |
|
9+ |
DBAPI 支持¶
以下方言/DBAPI 选项可用。请参考各个 DBAPI 章节以获取连接信息。
自动递增行为¶
SQLAlchemy Table 对象(包括整数主键)通常被假定具有 “自动递增” 行为,这意味着它们可以在 INSERT 时生成自己的主键值。对于在 Oracle 数据库中使用,有两个选项可用,即使用 IDENTITY 列(仅限 Oracle 数据库 12 及以上版本)或将 SEQUENCE 与列关联。
指定 GENERATED AS IDENTITY (Oracle 数据库 12 及以上版本)¶
从版本 12 开始,Oracle 数据库可以使用 identity 列,使用 Identity
来指定自动递增行为
t = Table(
"mytable",
metadata,
Column("id", Integer, Identity(start=3), primary_key=True),
Column(...),
...,
)
上述 Table
对象的 CREATE TABLE 语句将是
CREATE TABLE mytable (
id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 3),
...,
PRIMARY KEY (id)
)
Identity
对象支持许多选项来控制列的 “自动递增” 行为,例如起始值、递增值等。除了标准选项外,Oracle 数据库还支持将 Identity.always
设置为 None
以使用默认生成模式,在 DDL 中呈现 GENERATED AS IDENTITY。它还支持将 Identity.on_null
设置为 True
,以指定 ON NULL 并结合 ‘BY DEFAULT’ identity 列。
使用 SEQUENCE (所有 Oracle 数据库版本)¶
旧版本的 Oracle 数据库没有 “自动递增” 功能:SQLAlchemy 依赖于序列来生成这些值。对于旧版本的 Oracle 数据库,必须始终显式指定序列才能启用自动递增。这与大多数文档示例不同,后者假设使用具有自动递增功能的数据库。要指定序列,请使用 sqlalchemy.schema.Sequence 对象,该对象传递给 Column 构造
t = Table(
"mytable",
metadata,
Column("id", Integer, Sequence("id_seq", start=1), primary_key=True),
Column(...),
...,
)
当使用表反射时,即 autoload_with=engine,也需要此步骤
t = Table(
"mytable",
metadata,
Column("id", Integer, Sequence("id_seq", start=1), primary_key=True),
autoload_with=engine,
)
事务隔离级别 / 自动提交¶
Oracle 数据库支持 “READ COMMITTED” 和 “SERIALIZABLE” 隔离模式。python-oracledb 和 cx_Oracle 方言也支持 AUTOCOMMIT 隔离级别。
要使用每个连接的执行选项进行设置
connection = engine.connect()
connection = connection.execution_options(isolation_level="AUTOCOMMIT")
对于 READ COMMITTED
和 SERIALIZABLE
,Oracle 数据库方言在会话级别使用 ALTER SESSION
设置级别,当连接返回到连接池时,该级别将恢复为其默认设置。
isolation_level
的有效值包括
READ COMMITTED
AUTOCOMMIT
SERIALIZABLE
注意
Oracle 数据库方言实现的 Connection.get_isolation_level()
方法的实现必然强制启动事务,使用 Oracle 数据库 DBMS_TRANSACTION.LOCAL_TRANSACTION_ID 函数;否则通常无法读取级别。
此外,如果由于权限或其他原因导致 v$transaction
视图不可用,Connection.get_isolation_level()
方法将引发异常,这在 Oracle 数据库安装中很常见。
python-oracledb 和 cx_Oracle 方言尝试在方言首次连接到数据库时调用 Connection.get_isolation_level()
方法,以便获取 “默认” 隔离级别。此默认级别是必要的,以便在使用 Connection.execution_options()
方法临时修改连接后,可以在连接上重置级别。在常见情况下,由于 v$transaction
不可读以及任何其他数据库相关故障,Connection.get_isolation_level()
方法引发异常,级别被假定为 “READ COMMITTED”。对于此初始首次连接条件,不会发出警告,因为预计这是 Oracle 数据库上的常见限制。
在版本 1.3.16 中新增: 为 cx_Oracle 方言添加了对 AUTOCOMMIT 的支持,以及默认隔离级别的概念
在版本 1.3.21 中新增: 添加了对 SERIALIZABLE 的支持,以及隔离级别的实时读取。
在版本 1.3.22 中更改: 如果由于 v$transaction 视图的权限(在 Oracle 安装中很常见)而无法读取默认隔离级别,则默认隔离级别硬编码为 “READ COMMITTED”,这是 1.3.21 之前的行为。
标识符大小写¶
在 Oracle 数据库中,数据字典使用大写文本表示所有不区分大小写的标识符名称。这与 SQLAlchemy 的期望相矛盾,后者假定不区分大小写的名称表示为小写文本。
作为不区分大小写标识符名称的示例,请考虑下表
CREATE TABLE MyTable (Identifier INTEGER PRIMARY KEY)
如果您向 Oracle 数据库询问有关此表的信息,则表名将被报告为 MYTABLE
,列名将被报告为 IDENTIFIER
。与大多数其他数据库(如 PostgreSQL 和 MySQL)进行比较,它们会将这些名称报告为 mytable
和 identifier
。这些名称未被引用,因此不区分大小写。MyTable
和 Identifier
的特殊大小写只有当它们在表定义中被引用时才会被保留
CREATE TABLE "MyTable" ("Identifier" INTEGER PRIMARY KEY)
当构造 SQLAlchemy Table
对象时,全小写名称被认为是不区分大小写的。因此,下表假定不区分大小写名称
Table("mytable", metadata, Column("identifier", Integer, primary_key=True))
然而,当使用混合大小写或大写名称时,假定区分大小写
Table("MyTable", metadata, Column("Identifier", Integer, primary_key=True))
当发出文本 SQL SELECT 语句并在 DBAPI cursor.description
属性中查看列名时,数据库驱动程序级别也会发生类似情况。像 PostgreSQL 这样的数据库会将不区分大小写的名称规范化为小写
>>> pg_engine = create_engine("postgresql://scott:tiger@localhost/test")
>>> pg_connection = pg_engine.connect()
>>> result = pg_connection.exec_driver_sql("SELECT 1 AS SomeName")
>>> result.cursor.description
(Column(name='somename', type_code=23),)
然而,Oracle 将它们规范化为大写
>>> oracle_engine = create_engine("oracle+oracledb://scott:tiger@oracle18c/xe")
>>> oracle_connection = oracle_engine.connect()
>>> result = oracle_connection.exec_driver_sql(
... "SELECT 1 AS SomeName FROM DUAL"
... )
>>> result.cursor.description
[('SOMENAME', <DbType DB_TYPE_NUMBER>, 127, None, 0, -127, True)]
为了在表反射和 b. 仅文本 SQL 语句往返这两种情况下实现跨数据库对等性,当使用 Oracle 方言时,SQLAlchemy 执行一个名为 名称规范化 的步骤。此过程也可能适用于其他第三方方言,它们对不区分大小写的名称具有类似的大写处理。
当使用名称规范化时,SQLAlchemy 尝试检测名称是否不区分大小写,方法是检查是否所有字符都仅是大写字母;如果是,则它假定这是一个不区分大小写的名称,并以小写名称传递。
对于表反射,在 Oracle 数据库的目录表中看到表示为全大写的表名将被假定为不区分大小写的名称。这就是允许 Table
定义使用小写名称,并在 Oracle 数据库和所有其他数据库(如 PostgreSQL 和 MySQL)上从反射角度来看同样兼容的原因
# matches a table created with CREATE TABLE mytable
Table("mytable", metadata, autoload_with=some_engine)
上面,全小写名称 "mytable"
不区分大小写;它将匹配 PostgreSQL 报告为 "mytable"
的表和 Oracle 报告为 "MYTABLE"
的表。如果不存在名称规范化,则上述 Table
定义不可能以跨数据库的方式进行内省,因为我们正在处理一个不区分大小写的名称,但每个数据库的报告方式不同。
在这种情况下,可以强制启用区分大小写,例如,如果我们想用该精确大小写表示引用的表名 "MYTABLE"
,最简单的方法是直接使用该大小写,这将被视为区分大小写的名称
# matches a table created with CREATE TABLE "MYTABLE"
Table("MYTABLE", metadata, autoload_with=some_engine)
对于引用的全小写名称的特殊情况,可以使用 quoted_name
构造
from sqlalchemy import quoted_name
# matches a table created with CREATE TABLE "mytable"
Table(
quoted_name("mytable", quote=True), metadata, autoload_with=some_engine
)
当处理来自纯文本 SQL 字符串的结果集时,也会进行名称规范化,这些字符串没有其他与之关联的 Table
或 Column
元数据。这包括使用 Connection.exec_driver_sql()
执行的 SQL 字符串和使用不包含 Column
元数据的 text()
构造执行的 SQL 字符串。
返回到 Oracle 数据库 SELECT 语句,我们看到即使 cursor.description
将列名报告为 SOMENAME
,SQLAlchemy 名称也会将其规范化为 somename
>>> oracle_engine = create_engine("oracle+oracledb://scott:tiger@oracle18c/xe")
>>> oracle_connection = oracle_engine.connect()
>>> result = oracle_connection.exec_driver_sql(
... "SELECT 1 AS SomeName FROM DUAL"
... )
>>> result.cursor.description
[('SOMENAME', <DbType DB_TYPE_NUMBER>, 127, None, 0, -127, True)]
>>> result.keys()
RMKeyView(['somename'])
上述行为产生不准确结果的唯一情况是使用全大写、引用的名称时。SQLAlchemy 无法确定 cursor.description
中的特定名称是否被引用,因此区分大小写,或者未被引用,并且应该进行名称规范化
>>> result = oracle_connection.exec_driver_sql(
... 'SELECT 1 AS "SOMENAME" FROM DUAL'
... )
>>> result.cursor.description
[('SOMENAME', <DbType DB_TYPE_NUMBER>, 127, None, 0, -127, True)]
>>> result.keys()
RMKeyView(['somename'])
对于这种情况,SQLAlchemy 2.1 中将提供一个新功能,以在特定情况下禁用名称规范化行为。
最大标识符长度¶
SQLAlchemy 对 Oracle 数据库支持的最大标识符长度很敏感。这会影响生成的 SQL 标签名称以及约束名称的生成,特别是在使用 配置约束命名约定 中描述的约束命名约定功能的情况下。
Oracle 数据库 12.2 将默认最大标识符长度从 30 增加到 128。从 SQLAlchemy 1.4 开始,Oracle 方言的默认最大标识符长度为 128 个字符。首次连接时,将获得数据库实际支持的最大长度。在所有情况下,设置 create_engine.max_identifier_length
参数将绕过此更改,并将按原样使用给定的值
engine = create_engine(
"oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1",
max_identifier_length=30,
)
如果未设置 create_engine.max_identifier_length
,则 oracledb 方言在内部使用 python-oracledb 2.5 版本以来驱动程序连接上可用的 max_identifier_length
属性。当使用较旧的驱动程序版本或使用 cx_Oracle 方言时,SQLAlchemy 将改为尝试在首次连接时使用查询 SELECT value FROM v$parameter WHERE name = 'compatible'
,以确定数据库的有效兼容性版本。“兼容性”版本是一个版本号,它独立于实际的数据库版本。它用于协助数据库迁移。它由 Oracle 数据库初始化参数配置。然后,兼容性版本确定数据库允许的最大标识符长度。如果 V$ 视图不可用,则改为使用数据库版本信息。
最大标识符长度在生成 SELECT 语句中的匿名 SQL 标签时起作用,但在从命名约定生成约束名称时更为关键。正是这个领域使得 SQLAlchemy 需要保守地更改此默认值。例如,以下命名约定根据标识符长度生成两个非常不同的约束名称
from sqlalchemy import Column
from sqlalchemy import Index
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import Table
from sqlalchemy.dialects import oracle
from sqlalchemy.schema import CreateIndex
m = MetaData(naming_convention={"ix": "ix_%(column_0N_name)s"})
t = Table(
"t",
m,
Column("some_column_name_1", Integer),
Column("some_column_name_2", Integer),
Column("some_column_name_3", Integer),
)
ix = Index(
None,
t.c.some_column_name_1,
t.c.some_column_name_2,
t.c.some_column_name_3,
)
oracle_dialect = oracle.dialect(max_identifier_length=30)
print(CreateIndex(ix).compile(dialect=oracle_dialect))
当标识符长度为 30 时,上面的 CREATE INDEX 看起来像
CREATE INDEX ix_some_column_name_1s_70cd ON t
(some_column_name_1, some_column_name_2, some_column_name_3)
然而,当长度为 128 时,它变为
.. sourcecode:: sql
CREATE INDEX ix_some_column_name_1some_column_name_2some_column_name_3 ON t (some_column_name_1, some_column_name_2, some_column_name_3)
因此,在 Oracle 数据库 12.2 或更高版本上运行 SQLAlchemy 1.4 之前版本的应用程序可能会遇到数据库迁移的场景,该迁移希望对先前使用较短长度生成的名称执行 “DROP CONSTRAINT”。当在未首先调整索引或约束名称的情况下更改标识符长度时,此迁移将失败。强烈建议此类应用程序使用 create_engine.max_identifier_length
,以便保持对截断名称生成的控制,并在更改此值时在暂存环境中充分审查和测试所有数据库迁移,以确保已减轻此更改的影响。
在版本 1.4 中更改: Oracle 数据库的默认 max_identifier_length 为 128 个字符,如果 Oracle 数据库或其兼容性设置低于 12.2 版本,则在首次连接时将其向下调整为 30。
LIMIT/OFFSET/FETCH 支持¶
诸如 Select.limit()
和 Select.offset()
之类的方法使用 FETCH FIRST N ROW / OFFSET N ROWS
语法,假定为 Oracle 数据库 12c 或更高版本,并假定 SELECT 语句未嵌入到诸如 UNION 之类的复合语句中。通过使用 Select.fetch()
方法也可以直接使用此语法。
在版本 2.0 中更改: Oracle 数据库方言现在对所有 Select.limit()
和 Select.offset()
用法(包括在 ORM 和旧版 Query
中)使用 FETCH FIRST N ROW / OFFSET N ROWS
。要使用窗口函数强制旧版行为,请将 enable_offset_fetch=False
方言参数指定给 create_engine()
。
通过将 enable_offset_fetch=False
传递给 create_engine()
,可以在任何 Oracle 数据库版本上禁用 FETCH FIRST / OFFSET
的使用,这将强制使用利用窗口函数的 “旧版” 模式。当使用 12c 之前的 Oracle 数据库版本时,也会自动选择此模式。
当使用旧版模式,或者当带有 limit/offset 的 Select
语句嵌入到复合语句中时,将使用基于窗口函数的 LIMIT/OFFSET 模拟方法,这涉及使用 ROW_NUMBER
创建子查询,这容易导致性能问题以及复杂语句的 SQL 构造问题。但是,所有 Oracle 数据库版本都支持此方法。请参阅以下注释。
关于 LIMIT/OFFSET 模拟的注释(当无法使用 fetch() 方法时)¶
如果在 12c 之前的 Oracle 数据库版本上使用 Select.limit()
和 Select.offset()
,或者在 ORM 中使用 Query.limit()
和 Query.offset()
方法,则以下注释适用
SQLAlchemy 当前使用 ROWNUM 来实现 LIMIT/OFFSET;确切的方法取自 https://blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results 。
默认情况下不使用 “FIRST_ROWS()” 优化关键字。要启用此优化指令的使用,请将
optimize_limits=True
指定给create_engine()
。在版本 1.4 中更改: Oracle 数据库方言使用 “后编译” 方案呈现 limit/offset 整数值,该方案在将语句传递给游标执行之前直接呈现整数。
use_binds_for_limits
标志不再起作用。
RETURNING 支持¶
Oracle 数据库完全支持 RETURNING 用于使用单个绑定参数集合调用的 INSERT、UPDATE 和 DELETE 语句(即 cursor.execute()
样式语句;SQLAlchemy 通常不支持将 RETURNING 与 executemany 语句一起使用)。也可以返回多行。
在版本 2.0 中更改: Oracle 数据库后端完全支持 RETURNING,与其他后端保持一致。
ON UPDATE CASCADE¶
Oracle 数据库不具备原生的 ON UPDATE CASCADE 功能。基于触发器的解决方案请参考 https://web.archive.org/web/20090317041251/https://asktom.oracle.com/tkyte/update_cascade/index.html
当使用 SQLAlchemy ORM 时,ORM 在手动发出级联更新方面的能力有限 - 使用 “deferrable=True, initially=’deferred’” 关键字参数指定 ForeignKey 对象,并在每个 relationship() 上指定 “passive_updates=False”。
Oracle 数据库 8 兼容性¶
警告
SQLAlchemy 2.0 中 Oracle 数据库 8 的兼容性状态未知。
当检测到 Oracle 数据库 8 时,方言会在内部将自身配置为以下行为
同义词/DBLINK 反射¶
当将反射与 Table 对象一起使用时,方言可以选择搜索由同义词指示的表,无论是在本地还是远程模式中,或者通过 DBLINK 访问,方法是将标志 oracle_resolve_synonyms=True
作为关键字参数传递给 Table
构造函数
some_table = Table(
"some_table", autoload_with=some_engine, oracle_resolve_synonyms=True
)
当设置此标志时,给定的名称(例如上面的 some_table
)不仅会在 ALL_TABLES
视图中搜索,还会在 ALL_SYNONYMS
视图中搜索,以查看此名称是否实际上是另一个名称的同义词。如果找到同义词并且它引用了 DBLINK,则 Oracle 数据库方言知道如何使用 DBLINK 语法(例如 @dblink
)来定位表的信息。
oracle_resolve_synonyms
在接受反射参数的任何地方都被接受,包括诸如 MetaData.reflect()
和 Inspector.get_columns()
等方法。
如果未使用同义词,则应禁用此标志。
约束反射¶
Oracle 数据库方言可以返回有关外键、唯一约束和 CHECK 约束以及表索引的信息。
可以使用 Inspector.get_foreign_keys()
、Inspector.get_unique_constraints()
、Inspector.get_check_constraints()
和 Inspector.get_indexes()
获取有关这些约束的原始信息。
在版本 1.2 中更改: Oracle 数据库方言现在可以反射 UNIQUE 和 CHECK 约束。
当在 Table
级别使用反射时,Table
也将包含这些约束。
请注意以下注意事项
当使用
Inspector.get_check_constraints()
方法时,Oracle 数据库会为指定 “NOT NULL” 的列构建一个特殊的 “IS NOT NULL” 约束。默认情况下不返回此约束;要包含 “IS NOT NULL” 约束,请传递标志include_all=True
from sqlalchemy import create_engine, inspect engine = create_engine( "oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1" ) inspector = inspect(engine) all_check_constraints = inspector.get_check_constraints( "some_table", include_all=True )
在大多数情况下,当反射
Table
时,UNIQUE 约束将 不会 作为UniqueConstraint
对象提供,因为在大多数情况下,Oracle 数据库使用 UNIQUE 索引镜像唯一约束(例外情况似乎是当两个或多个唯一约束表示相同的列时);Table
将改为使用Index
并设置unique=True
标志来表示这些约束。Oracle 数据库为表的主键创建隐式索引;此索引 排除 在所有索引结果之外。
为索引反射的列列表将不包括以 SYS_NC 开头的列名。
具有 SYSTEM/SYSAUX 表空间的表名¶
Inspector.get_table_names()
和 Inspector.get_temp_table_names()
方法分别返回当前引擎的表名列表。这些方法也是在诸如 MetaData.reflect()
等操作中发生的反射的一部分。默认情况下,这些操作会从操作中排除 SYSTEM
和 SYSAUX
表空间。为了改变这一点,可以使用 exclude_tablespaces
参数在引擎级别更改排除的默认表空间列表
# exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM
e = create_engine(
"oracle+oracledb://scott:tiger@localhost:1521/?service_name=freepdb1",
exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"],
)
FLOAT / DOUBLE 支持和行为¶
SQLAlchemy Float
和 Double
数据类型是通用数据类型,它们解析为给定后端 “最不令人惊讶” 的数据类型。对于 Oracle 数据库,这意味着它们解析为 FLOAT
和 DOUBLE
类型
>>> from sqlalchemy import cast, literal, Float
>>> from sqlalchemy.dialects import oracle
>>> float_datatype = Float()
>>> print(cast(literal(5.0), float_datatype).compile(dialect=oracle.dialect()))
CAST(:param_1 AS FLOAT)
Oracle 的 FLOAT
/ DOUBLE
数据类型是 NUMBER
的别名。Oracle 数据库以完整精度(而不是浮点精度)存储 NUMBER
值,这意味着 FLOAT
/ DOUBLE
实际上并不像原生 FP 值那样工作。Oracle 数据库而是提供特殊的数据类型 BINARY_FLOAT
和 BINARY_DOUBLE
来传递真正的 4 字节和 8 字节 FP 值。
SQLAlchemy 使用 BINARY_FLOAT
和 BINARY_DOUBLE
直接支持这些数据类型。要在数据库无关的方式中使用 Float
或 Double
数据类型,同时允许 Oracle 后端利用这些类型之一,请使用 TypeEngine.with_variant()
方法来设置变体
>>> from sqlalchemy import cast, literal, Float
>>> from sqlalchemy.dialects import oracle
>>> float_datatype = Float().with_variant(oracle.BINARY_FLOAT(), "oracle")
>>> print(cast(literal(5.0), float_datatype).compile(dialect=oracle.dialect()))
CAST(:param_1 AS BINARY_FLOAT)
例如,要在 Table
定义中使用此数据类型
my_table = Table(
"my_table",
metadata,
Column(
"fp_data", Float().with_variant(oracle.BINARY_FLOAT(), "oracle")
),
)
DateTime 兼容性¶
Oracle 数据库没有称为 DATETIME
的数据类型,它只有 DATE
,实际上可以存储日期和时间值。因此,Oracle 数据库方言提供了一个类型 DATE
,它是 DateTime
的子类。此类型没有特殊行为,仅作为此类型的 “标记” 存在;此外,当反射数据库列且类型报告为 DATE
时,将使用支持时间的 DATE
类型。
Oracle 数据库表选项¶
CREATE TABLE 短语在 Oracle 数据库方言中支持以下选项,与 Table
构造函数结合使用
ON COMMIT
:Table( "some_table", metadata, ..., prefixes=["GLOBAL TEMPORARY"], oracle_on_commit="PRESERVE ROWS", )
COMPRESS
:Table( "mytable", metadata, Column("data", String(32)), oracle_compress=True ) Table("mytable", metadata, Column("data", String(32)), oracle_compress=6)
oracle_compress
参数接受整数压缩级别,或True
以使用默认压缩级别。TABLESPACE
:Table("mytable", metadata, ..., oracle_tablespace="EXAMPLE_TABLESPACE")
oracle_tablespace
参数指定要在其中创建表的表空间。当您想在用户默认表空间以外的表空间中创建表时,这非常有用。版本 2.0.37 中的新功能。
Oracle 数据库特定索引选项¶
位图索引¶
您可以指定 oracle_bitmap
参数来创建位图索引而不是 B 树索引
Index("my_index", my_table.c.data, oracle_bitmap=True)
位图索引不能是唯一的,也不能被压缩。SQLAlchemy 不会检查此类限制,只有数据库会检查。
索引压缩¶
Oracle 数据库为包含大量重复值的索引提供了更有效的存储模式。使用 oracle_compress
参数打开键压缩
Index("my_index", my_table.c.data, oracle_compress=True)
Index(
"my_index",
my_table.c.data1,
my_table.c.data2,
unique=True,
oracle_compress=1,
)
oracle_compress
参数接受一个整数,指定要压缩的前缀列数,或 True
以使用默认值(非唯一索引的所有列,唯一索引的所有列但最后一列)。
Oracle 数据库数据类型¶
与所有 SQLAlchemy 方言一样,已知对 Oracle 数据库有效的所有大写类型都可以从顶级方言导入,无论它们是来自 sqlalchemy.types
还是来自本地方言
from sqlalchemy.dialects.oracle import (
BFILE,
BLOB,
CHAR,
CLOB,
DATE,
DOUBLE_PRECISION,
FLOAT,
INTERVAL,
LONG,
NCLOB,
NCHAR,
NUMBER,
NVARCHAR,
NVARCHAR2,
RAW,
TIMESTAMP,
VARCHAR,
VARCHAR2,
)
版本 1.2.19 中的新功能: 将 NCHAR
添加到 Oracle 方言导出的数据类型列表中。
特定于 Oracle 数据库或具有 Oracle 特定构造参数的类型如下
对象名称 | 描述 |
---|---|
实现 Oracle |
|
实现 Oracle |
|
提供 Oracle 数据库 DATE 类型。 |
|
Oracle 数据库 FLOAT。 |
|
|
|
Oracle 数据库 ROWID 类型。 |
|
Oracle 数据库 |
- class sqlalchemy.dialects.oracle.BFILE¶
成员
-
method
sqlalchemy.dialects.oracle.BFILE.
__init__(length: int | None = None)¶ 继承自
sqlalchemy.types.LargeBinary.__init__
的LargeBinary
方法构造一个 LargeBinary 类型。
- 参数:
length¶ – 可选,用于 DDL 语句的列长度,对于那些接受长度的二进制类型,例如 MySQL BLOB 类型。
-
method
- class sqlalchemy.dialects.oracle.BINARY_DOUBLE¶
实现 Oracle
BINARY_DOUBLE
数据类型。此数据类型与 Oracle
DOUBLE
数据类型的不同之处在于,它传递真正的 8 字节 FP 值。可以使用TypeEngine.with_variant()
将此数据类型与通用Double
数据类型结合使用。另请参阅
成员
-
method
sqlalchemy.dialects.oracle.BINARY_DOUBLE.
__init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)¶ 继承自
sqlalchemy.types.Float.__init__
的Float
方法构造一个 Float。
- 参数:
precision¶ –
用于 DDL
CREATE TABLE
的数字精度。后端 应该 尝试确保此精度指示通用Float
数据类型的位数。注意
对于 Oracle 数据库后端,在呈现 DDL 时不接受
Float.precision
参数,因为 Oracle 数据库不支持指定为十进制位数的浮点精度。而是使用 Oracle 数据库特定的FLOAT
数据类型并指定FLOAT.binary_precision
参数。这是 SQLAlchemy 2.0 版本中的新功能。要创建一个数据库无关的
Float
,它为 Oracle 数据库单独指定二进制精度,请使用TypeEngine.with_variant()
,如下所示from sqlalchemy import Column from sqlalchemy import Float from sqlalchemy.dialects import oracle Column( "float_data", Float(5).with_variant(oracle.FLOAT(binary_precision=16), "oracle"), )
asdecimal¶ – 与
Numeric
的标志相同,但默认为False
。请注意,将此标志设置为True
会导致浮点转换。decimal_return_scale¶ – 从浮点数转换为 Python 十进制数时要使用的默认比例。由于十进制不精确,浮点值通常会更长,并且大多数浮点数据库类型都没有 “比例” 的概念,因此默认情况下,浮点类型在转换时会查找前十个十进制位。指定此值将覆盖该长度。请注意,MySQL 浮点类型(确实包含 “比例”)如果未另行指定,则将 “比例” 用作 decimal_return_scale 的默认值。
-
method
- class sqlalchemy.dialects.oracle.BINARY_FLOAT¶
实现 Oracle
BINARY_FLOAT
数据类型。此数据类型与 Oracle
FLOAT
数据类型的不同之处在于,它传递真正的 4 字节 FP 值。可以使用TypeEngine.with_variant()
将此数据类型与通用Float
数据类型结合使用。另请参阅
成员
-
method
sqlalchemy.dialects.oracle.BINARY_FLOAT.
__init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)¶ 继承自
sqlalchemy.types.Float.__init__
的Float
方法构造一个 Float。
- 参数:
precision¶ –
用于 DDL
CREATE TABLE
的数字精度。后端 应该 尝试确保此精度指示通用Float
数据类型的位数。注意
对于 Oracle 数据库后端,在呈现 DDL 时不接受
Float.precision
参数,因为 Oracle 数据库不支持指定为十进制位数的浮点精度。而是使用 Oracle 数据库特定的FLOAT
数据类型并指定FLOAT.binary_precision
参数。这是 SQLAlchemy 2.0 版本中的新功能。要创建一个数据库无关的
Float
,它为 Oracle 数据库单独指定二进制精度,请使用TypeEngine.with_variant()
,如下所示from sqlalchemy import Column from sqlalchemy import Float from sqlalchemy.dialects import oracle Column( "float_data", Float(5).with_variant(oracle.FLOAT(binary_precision=16), "oracle"), )
asdecimal¶ – 与
Numeric
的标志相同,但默认为False
。请注意,将此标志设置为True
会导致浮点转换。decimal_return_scale¶ – 从浮点数转换为 Python 十进制数时要使用的默认比例。由于十进制不精确,浮点值通常会更长,并且大多数浮点数据库类型都没有 “比例” 的概念,因此默认情况下,浮点类型在转换时会查找前十个十进制位。指定此值将覆盖该长度。请注意,MySQL 浮点类型(确实包含 “比例”)如果未另行指定,则将 “比例” 用作 decimal_return_scale 的默认值。
-
method
- class sqlalchemy.dialects.oracle.DATE¶
提供 Oracle 数据库 DATE 类型。
此类型没有特殊的 Python 行为,只是它是
DateTime
的子类;这是为了适应 Oracle 数据库DATE
类型支持时间值的事实。成员
类签名
class
sqlalchemy.dialects.oracle.DATE
(sqlalchemy.dialects.oracle.types._OracleDateLiteralRender
,sqlalchemy.types.DateTime
)
- class sqlalchemy.dialects.oracle.FLOAT¶
Oracle 数据库 FLOAT。
这与
FLOAT
相同,不同之处在于接受 Oracle 数据库特定的FLOAT.binary_precision
参数,并且不接受Float.precision
参数。Oracle 数据库 FLOAT 类型以 “二进制精度” 指示精度,默认为 126。对于 REAL 类型,该值为 63。此参数不能完全映射到特定的小数位数,但大致相当于所需的小数位数除以 0.3103。
版本 2.0 中的新功能。
成员
-
method
sqlalchemy.dialects.oracle.FLOAT.
__init__(binary_precision=None, asdecimal=False, decimal_return_scale=None)¶ 构造 FLOAT
- 参数:
binary_precision¶ – Oracle 数据库二进制精度值,将在 DDL 中呈现。这可以近似为十进制字符的数量,使用公式 “十进制精度 = 0.30103 * 二进制精度”。Oracle 数据库用于 FLOAT / DOUBLE PRECISION 的默认值为 126。
asdecimal¶ – 参见
Float.asdecimal
decimal_return_scale¶ – 参见
Float.decimal_return_scale
-
method
- class sqlalchemy.dialects.oracle.INTERVAL¶
成员
类签名
class
sqlalchemy.dialects.oracle.INTERVAL
(sqlalchemy.types.NativeForEmulated
,sqlalchemy.types._AbstractInterval
)-
method
sqlalchemy.dialects.oracle.INTERVAL.
__init__(day_precision=None, second_precision=None)¶ 构造一个 INTERVAL。
请注意,目前仅支持 DAY TO SECOND 区间。这是由于可用的 DBAPI 中缺乏对 YEAR TO MONTH 区间的支持。
-
method
- class sqlalchemy.dialects.oracle.NCLOB¶
成员
-
method
sqlalchemy.dialects.oracle.NCLOB.
__init__(length: int | None = None, collation: str | None = None)¶ 继承自
sqlalchemy.types.String.__init__
方法,来自String
创建一个字符串持有类型。
- 参数:
length¶ – 可选,列的长度,用于 DDL 和 CAST 表达式。如果没有发出
CREATE TABLE
,则可以安全地省略。某些数据库可能需要length
用于 DDL,并且如果包含没有长度的VARCHAR
,则在发出CREATE TABLE
DDL 时会引发异常。该值是被解释为字节还是字符取决于数据库的具体情况。collation¶ –
可选,列级别的排序规则,用于 DDL 和 CAST 表达式。使用 SQLite、MySQL 和 PostgreSQL 支持的 COLLATE 关键字呈现。例如:
>>> from sqlalchemy import cast, select, String >>> print(select(cast("some string", String(collation="utf8"))))
SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1注意
在大多数情况下,
Unicode
或UnicodeText
数据类型应用于期望存储非 ASCII 数据的Column
。这些数据类型将确保在数据库上使用正确的类型。
-
method
-
attribute
sqlalchemy.dialects.oracle..
sqlalchemy.dialects.oracle.NVARCHAR2¶ NVARCHAR
的别名
- class sqlalchemy.dialects.oracle.NUMBER¶
- class sqlalchemy.dialects.oracle.LONG¶
成员
类签名
class
sqlalchemy.dialects.oracle.LONG
(sqlalchemy.types.Text
)-
method
sqlalchemy.dialects.oracle.LONG.
__init__(length: int | None = None, collation: str | None = None)¶ 继承自
sqlalchemy.types.String.__init__
方法,来自String
创建一个字符串持有类型。
- 参数:
length¶ – 可选,列的长度,用于 DDL 和 CAST 表达式。如果没有发出
CREATE TABLE
,则可以安全地省略。某些数据库可能需要length
用于 DDL,并且如果包含没有长度的VARCHAR
,则在发出CREATE TABLE
DDL 时会引发异常。该值是被解释为字节还是字符取决于数据库的具体情况。collation¶ –
可选,列级别的排序规则,用于 DDL 和 CAST 表达式。使用 SQLite、MySQL 和 PostgreSQL 支持的 COLLATE 关键字呈现。例如:
>>> from sqlalchemy import cast, select, String >>> print(select(cast("some string", String(collation="utf8"))))
SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1注意
在大多数情况下,
Unicode
或UnicodeText
数据类型应用于期望存储非 ASCII 数据的Column
。这些数据类型将确保在数据库上使用正确的类型。
-
method
- class sqlalchemy.dialects.oracle.RAW¶
类签名
class
sqlalchemy.dialects.oracle.RAW
(sqlalchemy.types._Binary
)
- class sqlalchemy.dialects.oracle.ROWID¶
Oracle 数据库 ROWID 类型。
当在 cast() 或类似函数中使用时,生成 ROWID。
- class sqlalchemy.dialects.oracle.TIMESTAMP¶
Oracle 数据库
TIMESTAMP
的实现,它支持额外的 Oracle 数据库特定模式版本 2.0 中的新功能。
成员
-
method
sqlalchemy.dialects.oracle.TIMESTAMP.
__init__(timezone: bool = False, local_timezone: bool = False)¶ 构造一个新的
TIMESTAMP
。
-
method
python-oracledb¶
通过 python-oracledb 驱动程序支持 Oracle 数据库。
DBAPI¶
python-oracledb 的文档和下载信息(如果适用)可在以下网址获得:https://oracle.github.io/python-oracledb/
连接¶
连接字符串
oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]
描述¶
Python-oracledb 是 Oracle 数据库的 Python 驱动程序。它具有默认的 “thin” 客户端模式,无需依赖项,以及可选的 “thick” 模式,它使用 Oracle 客户端库。它支持 SQLAlchemy 功能,包括两阶段事务和 Asyncio。
Python-oracle 是重命名和更新的 cx_Oracle 驱动程序。Oracle 不再在 cx_Oracle 命名空间中发布任何版本。
SQLAlchemy oracledb
方言在同一方言名称下提供同步和异步实现。正确的版本根据引擎的创建方式选择
使用
oracle+oracledb://...
调用create_engine()
将自动选择同步版本from sqlalchemy import create_engine sync_engine = create_engine( "oracle+oracledb://scott:tiger@localhost?service_name=FREEPDB1" )
使用
oracle+oracledb://...
调用create_async_engine()
将自动选择异步版本from sqlalchemy.ext.asyncio import create_async_engine asyncio_engine = create_async_engine( "oracle+oracledb://scott:tiger@localhost?service_name=FREEPDB1" )
异步版本的方言也可以使用
oracledb_async
后缀显式指定from sqlalchemy.ext.asyncio import create_async_engine asyncio_engine = create_async_engine( "oracle+oracledb_async://scott:tiger@localhost?service_name=FREEPDB1" )
2.0.25 版本新增: 添加了对 oracledb 异步版本的支持。
Thick 模式支持¶
默认情况下,python-oracledb 驱动程序在 “thin” 模式下运行,不需要安装 Oracle 客户端库。该驱动程序还支持 “thick” 模式,该模式使用 Oracle 客户端库来获得诸如 Oracle Application Continuity 之类的功能。
要启用 thick 模式,请显式调用 oracledb.init_oracle_client(),或将参数 thick_mode=True
传递给 create_engine()
。要将自定义参数传递给 init_oracle_client()
,例如 lib_dir
路径,可以传递一个字典,例如
engine = sa.create_engine(
"oracle+oracledb://...",
thick_mode={
"lib_dir": "/path/to/oracle/client/lib",
"config_dir": "/path/to/network_config_file_directory",
"driver_name": "my-app : 1.0.0",
},
)
请注意,传递 lib_dir
路径仅应在 macOS 或 Windows 上完成。在 Linux 上,它的行为不如您预期的那样。
另请参阅
python-oracledb 文档 启用 python-oracledb Thick 模式
连接到 Oracle 数据库¶
python-oracledb 提供了几种指示目标数据库的方法。该方言从一系列不同的 URL 形式进行转换。
给定目标数据库的主机名、端口和服务名称,您可以使用 service_name
查询字符串参数在 SQLAlchemy 中连接
engine = create_engine(
"oracle+oracledb://scott:tiger@hostname:port?service_name=myservice"
)
使用 Easy Connect 字符串连接¶
您可以将任何有效的 python-oracledb 连接字符串作为 create_engine.connect_args
字典中的 dsn
键值传递。请参阅 python-oracledb 文档 Oracle Net Services 连接字符串。
例如,要使用 Easy Connect 字符串 和超时来防止在 30 秒内无法建立到数据库的网络传输时连接建立挂起,并设置 60 秒的 keep-alive 时间以阻止空闲网络连接被防火墙终止
e = create_engine(
"oracle+oracledb://@",
connect_args={
"user": "scott",
"password": "tiger",
"dsn": "hostname:port/myservice?transport_connect_timeout=30&expire_time=60",
},
)
Easy Connect 语法在 Oracle 数据库的生命周期中得到了增强。查看您的数据库版本的文档。当前的文档位于 理解 Easy Connect 命名方法。
通用语法类似于
[[protocol:]//]host[:port][/[service_name]][?parameter_name=value{¶meter_name=value}]
请注意,尽管 SQLAlchemy URL 语法 hostname:port/dbname
看起来像 Oracle 的 Easy Connect 语法,但它是不同的。SQLAlchemy 的 URL 需要一个系统标识符 (SID) 作为 dbname
组件
engine = create_engine("oracle+oracledb://scott:tiger@hostname:port/sid")
Easy Connect 语法不支持 SID。它使用服务名称,这是连接到 Oracle 数据库的首选方法。
传递 python-oracledb 连接参数¶
其他 python-oracledb 驱动程序 连接选项 可以在 connect_args
中传递。例如
e = create_engine(
"oracle+oracledb://@",
connect_args={
"user": "scott",
"password": "tiger",
"dsn": "hostname:port/myservice",
"events": True,
"mode": oracledb.AUTH_MODE_SYSDBA,
},
)
使用 tnsnames.ora TNS 别名连接¶
如果没有提供端口、数据库名称或服务名称,则方言将使用 Oracle 数据库 DSN “连接字符串”。这会将 URL 的 “hostname” 部分作为数据源名称。例如,如果 tnsnames.ora
文件包含如下 TNS 别名 myalias
myalias =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orclpdb1)
)
)
当 myalias
是 URL 的主机名部分,并且未指定端口、数据库名称或 service_name
时,python-oracledb 方言连接到此数据库服务
engine = create_engine("oracle+oracledb://scott:tiger@myalias")
连接到 Oracle Autonomous Database¶
Oracle Autonomous Database 的用户应使用上面显示的 TNS 别名 URL,或将 TNS 别名作为 create_engine.connect_args
字典中的 dsn
键值传递。
如果 Oracle Autonomous Database 配置为相互 TLS (“mTLS”) 连接,则需要额外的配置,如 连接到 Oracle Cloud Autonomous Databases 中所示。总而言之,Thick 模式用户应配置文件位置并在 sqlnet.ora
中适当设置钱包路径
e = create_engine(
"oracle+oracledb://@",
thick_mode={
# directory containing tnsnames.ora and cwallet.so
"config_dir": "/opt/oracle/wallet_dir",
},
connect_args={
"user": "scott",
"password": "tiger",
"dsn": "mydb_high",
},
)
mTLS 的 Thin 模式用户应在创建引擎时传递适当的目录和 PEM 钱包密码,类似于
e = create_engine(
"oracle+oracledb://@",
connect_args={
"user": "scott",
"password": "tiger",
"dsn": "mydb_high",
"config_dir": "/opt/oracle/wallet_dir", # directory containing tnsnames.ora
"wallet_location": "/opt/oracle/wallet_dir", # directory containing ewallet.pem
"wallet_password": "top secret", # password for the PEM file
},
)
通常 config_dir
和 wallet_location
是同一个目录,即 Oracle Autonomous Database 钱包 zip 文件解压缩到的位置。请注意,此目录应受到保护。
连接池¶
具有多个并发用户的应用程序应使用连接池。最小尺寸的连接池对于不经常使用连接的长时间运行的单用户应用程序也很有益处。
python-oracledb 驱动程序提供了自己的连接池实现,可以代替 SQLAlchemy 的池功能使用。驱动程序池为高可用性功能提供支持,例如死连接检测、计划数据库停机时的连接耗尽、对 Oracle Application Continuity 和 Transparent Application Continuity 的支持,并支持 数据库驻留连接池 (DRCP)。
要利用 python-oracledb 的池,请使用 create_engine.creator
参数提供一个返回新连接的函数,同时将 create_engine.pool_class
设置为 NullPool
以禁用 SQLAlchemy 的池
import oracledb
from sqlalchemy import create_engine
from sqlalchemy import text
from sqlalchemy.pool import NullPool
# Uncomment to use the optional python-oracledb Thick mode.
# Review the python-oracledb doc for the appropriate parameters
# oracledb.init_oracle_client(<your parameters>)
pool = oracledb.create_pool(
user="scott",
password="tiger",
dsn="localhost:1521/freepdb1",
min=1,
max=4,
increment=1,
)
engine = create_engine(
"oracle+oracledb://", creator=pool.acquire, poolclass=NullPool
)
然后可以正常使用上述引擎。在内部,python-oracledb 处理连接池
with engine.connect() as conn:
print(conn.scalar(text("select 1 from dual")))
有关创建连接池时可以使用的参数,请参阅 python-oracledb 文档 oracledb.create_pool()。
使用 Oracle 数据库驻留连接池 (DRCP)¶
当使用 Oracle 数据库的数据库驻留连接池 (DRCP) 时,最佳实践是指定连接类和 “纯度”。请参阅 python-oracledb 文档关于 DRCP。例如
import oracledb
from sqlalchemy import create_engine
from sqlalchemy import text
from sqlalchemy.pool import NullPool
# Uncomment to use the optional python-oracledb Thick mode.
# Review the python-oracledb doc for the appropriate parameters
# oracledb.init_oracle_client(<your parameters>)
pool = oracledb.create_pool(
user="scott",
password="tiger",
dsn="localhost:1521/freepdb1",
min=1,
max=4,
increment=1,
cclass="MYCLASS",
purity=oracledb.PURITY_SELF,
)
engine = create_engine(
"oracle+oracledb://", creator=pool.acquire, poolclass=NullPool
)
然后可以正常使用上述引擎,其中 python-oracledb 处理应用程序连接池,而 Oracle 数据库另外使用 DRCP
with engine.connect() as conn:
print(conn.scalar(text("select 1 from dual")))
如果您希望为不同的连接使用不同的连接类或纯度,请包装 pool.acquire()
import oracledb
from sqlalchemy import create_engine
from sqlalchemy import text
from sqlalchemy.pool import NullPool
# Uncomment to use python-oracledb Thick mode.
# Review the python-oracledb doc for the appropriate parameters
# oracledb.init_oracle_client(<your parameters>)
pool = oracledb.create_pool(
user="scott",
password="tiger",
dsn="localhost:1521/freepdb1",
min=1,
max=4,
increment=1,
cclass="MYCLASS",
purity=oracledb.PURITY_SELF,
)
def creator():
return pool.acquire(cclass="MYOTHERCLASS", purity=oracledb.PURITY_NEW)
engine = create_engine(
"oracle+oracledb://", creator=creator, poolclass=NullPool
)
SQLAlchemy oracledb 方言在驱动程序外部使用的引擎选项¶
SQLAlchemy oracledb 方言本身也使用一些选项。这些选项始终直接传递给 create_engine()
,例如
e = create_engine("oracle+oracledb://user:pass@tnsalias", arraysize=500)
oracledb 方言接受的参数如下
Unicode¶
与 Python 3 下的所有 DBAPI 一样,所有字符串本质上都是 Unicode 字符串。
确保正确的客户端编码¶
在 python-oracledb 中,用于所有字符数据的编码是 “UTF-8”。
Unicode 特定列数据类型¶
Core 表达式语言通过使用 Unicode
和 UnicodeText
数据类型来处理 Unicode 数据。这些类型默认对应于 VARCHAR2 和 CLOB Oracle 数据库数据类型。当将这些数据类型与 Unicode 数据一起使用时,预计数据库配置了 Unicode 感知的字符集,以便 VARCHAR2 和 CLOB 数据类型可以容纳数据。
如果 Oracle 数据库未配置 Unicode 字符集,则有两个选项:显式使用 NCHAR
和 NCLOB
数据类型,或者将标志 use_nchar_for_unicode=True
传递给 create_engine()
,这将导致 SQLAlchemy 方言对 Unicode
/ UnicodeText
数据类型使用 NCHAR/NCLOB,而不是 VARCHAR/CLOB。
版本 1.3 中变更:Unicode
和 UnicodeText
数据类型现在对应于 VARCHAR2
和 CLOB
Oracle 数据库数据类型,除非在调用 create_engine()
时,将 use_nchar_for_unicode=True
传递给方言。
编码错误¶
对于 Oracle 数据库中出现编码损坏数据的异常情况,方言接受一个参数 encoding_errors
,该参数将传递给 Unicode 解码函数,以影响如何处理解码错误。该值最终由 Python 的 decode 函数使用,并通过 python-oracledb 的 encodingErrors
参数(由 Cursor.var()
使用)以及 SQLAlchemy 自己的解码函数传递,因为 python-oracledb 方言在不同情况下都会使用这两者。
版本 1.3.11 中的新功能。
使用 setinputsizes 对 python-oracledb 数据绑定进行细粒度控制¶
python-oracle DBAPI 深度且根本地依赖于 DBAPI setinputsizes()
调用的使用。此调用的目的是为作为参数传递的 Python 值建立绑定到 SQL 语句的数据类型。虽然几乎没有其他 DBAPI 为 setinputsizes()
调用分配任何用途,但 python-oracledb DBAPI 在其与 Oracle 数据库的交互中严重依赖它,并且在某些情况下,SQLAlchemy 无法确切知道应如何绑定数据,因为某些设置可能会导致截然不同的性能特征,同时改变类型强制行为。
强烈建议 oracledb 方言的用户阅读 python-oracledb 在 数据库类型 中内置数据类型符号列表。请注意,在某些情况下,使用这些类型与不使用这些类型相比,可能会发生显著的性能下降。
在 SQLAlchemy 方面,DialectEvents.do_setinputsizes()
事件既可以用于运行时可见性(例如,日志记录)setinputsizes 步骤,也可以完全控制如何在每个语句的基础上使用 setinputsizes()
。
版本 1.2.9 中的新功能:添加了 DialectEvents.setinputsizes()
示例 1 - 记录所有 setinputsizes 调用¶
以下示例说明了如何从 SQLAlchemy 的角度记录中间值,然后再将其转换为原始 setinputsizes()
参数字典。字典的键是 BindParameter
对象,这些对象具有 .key
和 .type
属性
from sqlalchemy import create_engine, event
engine = create_engine(
"oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1"
)
@event.listens_for(engine, "do_setinputsizes")
def _log_setinputsizes(inputsizes, cursor, statement, parameters, context):
for bindparam, dbapitype in inputsizes.items():
log.info(
"Bound parameter name: %s SQLAlchemy type: %r DBAPI object: %s",
bindparam.key,
bindparam.type,
dbapitype,
)
示例 2 - 删除所有 CLOB 的绑定¶
为了提高性能,从 Oracle 数据库获取 LOB 数据类型在 SQLAlchemy 中默认设置为 Text
类型。此设置可以按如下方式修改
from sqlalchemy import create_engine, event
from oracledb import CLOB
engine = create_engine(
"oracle+oracledb://scott:tiger@localhost:1521?service_name=freepdb1"
)
@event.listens_for(engine, "do_setinputsizes")
def _remove_clob(inputsizes, cursor, statement, parameters, context):
for bindparam, dbapitype in list(inputsizes.items()):
if dbapitype is CLOB:
del inputsizes[bindparam]
LOB 数据类型¶
LOB 数据类型是指“大对象”数据类型,例如 CLOB、NCLOB 和 BLOB。Oracle 数据库可以有效地将这些数据类型作为单个缓冲区返回。SQLAlchemy 默认使用类型处理程序来执行此操作。
要禁用类型处理程序的使用,并将 LOB 对象作为具有 read()
方法的经典缓冲对象传递,可以将参数 auto_convert_lobs=False
传递给 create_engine()
。
RETURNING 支持¶
oracledb 方言使用 OUT 参数实现 RETURNING。该方言完全支持 RETURNING。
两阶段事务支持¶
python-oracledb 完全支持两阶段事务。(Thin 模式需要 python-oracledb 2.3)。两阶段事务的 API 在 Core 级别通过 Connection.begin_twophase()
和 Session.twophase
提供,用于透明的 ORM 使用。
版本 2.0.32 中变更:添加了对两阶段事务的支持
精确数值¶
SQLAlchemy 的数值类型可以处理接收和返回作为 Python Decimal
对象或浮点数对象的值。当使用 Numeric
对象或子类(例如 Float
、DOUBLE_PRECISION
等)时,Numeric.asdecimal
标志确定是否应在返回时将值强制转换为 Decimal
,还是作为浮点数对象返回。为了使情况在 Oracle 数据库下更加复杂,如果 “scale” 为零,则 NUMBER
类型也可以表示整数值,因此 Oracle 数据库特定的 NUMBER
类型也考虑到了这一点。
oracledb 方言广泛使用连接级和游标级 “outputtypehandler” 可调用对象,以便根据请求强制转换数值。这些可调用对象特定于正在使用的 Numeric
的特定风格,以及是否没有 SQLAlchemy 类型对象。在某些观察到的场景中,Oracle 数据库可能会发送关于返回的数值类型的不完整或不明确的信息,例如查询中的数值类型埋藏在多个子查询级别之下。类型处理程序尽最大努力在所有情况下做出正确的决定,并将所有驱动程序可以做出最佳决定的情况推迟到基础的 python-oracledb DBAPI。
当没有类型对象时,例如在执行纯 SQL 字符串时,会存在一个默认的 “outputtypehandler”,它通常会将指定精度和比例的数值作为 Python Decimal
对象返回。为了性能原因禁用此强制转换为十进制,请将标志 coerce_to_decimal=False
传递给 create_engine()
engine = create_engine(
"oracle+oracledb://scott:tiger@tnsalias", coerce_to_decimal=False
)
coerce_to_decimal
标志仅影响未与 Numeric
SQLAlchemy 类型(或此类类型的子类)关联的纯字符串 SQL 语句的结果。
版本 1.2 中变更:oracle 方言的数值处理系统已经过重新设计,以利用更新的驱动程序功能以及更好地集成 outputtypehandlers。
版本 2.0.0 中的新功能:添加了对 python-oracledb 驱动程序的支持。
cx_Oracle¶
通过 cx-Oracle 驱动程序支持 Oracle 数据库数据库。
DBAPI¶
cx-Oracle 的文档和下载信息(如果适用)可在以下网址获取:https://oracle.github.io/python-cx_Oracle/
连接¶
连接字符串
oracle+cx_oracle://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]
描述¶
cx_Oracle 是 Oracle 数据库的原始驱动程序。它已被 python-oracledb 取代,应改为使用 python-oracledb。
DSN 与主机名连接¶
cx_Oracle 提供了几种指示目标数据库的方法。方言从一系列不同的 URL 形式进行转换。
使用 Easy Connect 语法的主机名连接¶
给定目标数据库的主机名、端口和服务名称,例如来自 Oracle 数据库的 Easy Connect 语法,然后在 SQLAlchemy 中使用 service_name
查询字符串参数进行连接
engine = create_engine(
"oracle+cx_oracle://scott:tiger@hostname:port?service_name=myservice&encoding=UTF-8&nencoding=UTF-8"
)
请注意,cx_Oracle 8.0 中编码和 nencoding 的默认驱动程序值已更改为 “UTF-8”,因此在使用该版本或更高版本时可以省略这些参数。
要使用完整的 Easy Connect 字符串,请将其作为 create_engine.connect_args
字典中的 dsn
键值传递
import cx_Oracle
e = create_engine(
"oracle+cx_oracle://@",
connect_args={
"user": "scott",
"password": "tiger",
"dsn": "hostname:port/myservice?transport_connect_timeout=30&expire_time=60",
},
)
使用 tnsnames.ora 或连接到 Oracle Autonomous Database¶
或者,如果没有提供端口、数据库名称或服务名称,则方言将使用 Oracle 数据库 DSN “连接字符串”。这会将 URL 的 “hostname” 部分作为数据源名称。例如,如果 tnsnames.ora
文件包含如下所示的 TNS 别名 myalias
myalias =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orclpdb1)
)
)
当 myalias
是 URL 的主机名部分时,cx_Oracle 方言连接到此数据库服务,而无需指定端口、数据库名称或 service_name
engine = create_engine("oracle+cx_oracle://scott:tiger@myalias")
Oracle Autonomous Database 的用户应使用此语法。如果数据库配置为 mutural TLS (“mTLS”),则还必须配置云钱包,如 cx_Oracle 文档 连接到 Autonomous Databases 中所示。
SID 连接¶
要使用 Oracle 数据库的已过时的系统标识符连接语法,可以在 URL 的 “database name” 部分中传递 SID
engine = create_engine(
"oracle+cx_oracle://scott:tiger@hostname:port/dbname"
)
在上面,传递给 cx_Oracle 的 DSN 由 cx_Oracle.makedsn()
创建,如下所示
>>> import cx_Oracle
>>> cx_Oracle.makedsn("hostname", 1521, sid="dbname")
'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=hostname)(PORT=1521))(CONNECT_DATA=(SID=dbname)))'
请注意,虽然 SQLAlchemy 语法 hostname:port/dbname
看起来像 Oracle 的 Easy Connect 语法,但它有所不同。它使用 SID 代替 Easy Connect 所需的服务名称。Easy Connect 语法不支持 SID。
传递 cx_Oracle 连接参数¶
其他连接参数通常可以通过 URL 查询字符串传递;特定的符号(如 SYSDBA
)会被拦截并转换为正确的符号
e = create_engine(
"oracle+cx_oracle://user:pass@dsn?encoding=UTF-8&nencoding=UTF-8&mode=SYSDBA&events=true"
)
版本 1.3 中变更:cx_Oracle 方言现在接受 URL 字符串本身中的所有参数名称,以传递给 cx_Oracle DBAPI。正如早期的情况,但未正确记录,create_engine.connect_args
参数也接受所有 cx_Oracle DBAPI 连接参数。
要将参数直接传递给 .connect()
而不使用查询字符串,请使用 create_engine.connect_args
字典。可以传递任何 cx_Oracle 参数值和/或常量,例如
import cx_Oracle
e = create_engine(
"oracle+cx_oracle://user:pass@dsn",
connect_args={
"encoding": "UTF-8",
"nencoding": "UTF-8",
"mode": cx_Oracle.SYSDBA,
"events": True,
},
)
请注意,cx_Oracle 8.0 中 encoding
和 nencoding
的默认驱动程序值已更改为 “UTF-8”,因此在使用该版本或更高版本时可以省略这些参数。
SQLAlchemy cx_Oracle 方言在驱动程序外部使用的选项¶
还有一些选项由 SQLAlchemy cx_oracle 方言本身使用。这些选项始终直接传递给 create_engine()
,例如
e = create_engine(
"oracle+cx_oracle://user:pass@dsn", coerce_to_decimal=False
)
cx_oracle 方言接受的参数如下
使用 cx_Oracle SessionPool¶
cx_Oracle 驱动程序提供了自己的连接池实现,可以代替 SQLAlchemy 的池功能使用。驱动程序池支持 Oracle 数据库功能,例如死连接检测、计划数据库停机时的连接耗尽、对 Oracle Application Continuity 和 Transparent Application Continuity 的支持,并支持数据库驻留连接池 (DRCP)。
使用驱动程序池可以通过使用 create_engine.creator
参数来提供返回新连接的函数,以及将 create_engine.pool_class
设置为 NullPool
以禁用 SQLAlchemy 的池化来实现
import cx_Oracle
from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
pool = cx_Oracle.SessionPool(
user="scott",
password="tiger",
dsn="orclpdb",
min=1,
max=4,
increment=1,
threaded=True,
encoding="UTF-8",
nencoding="UTF-8",
)
engine = create_engine(
"oracle+cx_oracle://", creator=pool.acquire, poolclass=NullPool
)
然后可以像往常一样使用上面的引擎,其中 cx_Oracle 的池处理连接池
with engine.connect() as conn:
print(conn.scalar("select 1 from dual"))
除了为多用户应用程序提供可扩展的解决方案外,cx_Oracle 会话池还支持一些 Oracle 功能,例如 DRCP 和 Application Continuity。
请注意,池创建参数 threaded
、encoding
和 nencoding
在较新的 cx_Oracle 版本中已弃用。
使用 Oracle 数据库驻留连接池 (DRCP)¶
当使用 Oracle 数据库的 DRCP 时,最佳实践是在从 SessionPool 获取连接时传递连接类和 “purity”。请参阅 cx_Oracle DRCP 文档。
这可以通过包装 pool.acquire()
来实现
import cx_Oracle
from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
pool = cx_Oracle.SessionPool(
user="scott",
password="tiger",
dsn="orclpdb",
min=2,
max=5,
increment=1,
threaded=True,
encoding="UTF-8",
nencoding="UTF-8",
)
def creator():
return pool.acquire(
cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF
)
engine = create_engine(
"oracle+cx_oracle://", creator=creator, poolclass=NullPool
)
然后可以像往常一样使用上面的引擎,其中 cx_Oracle 处理会话池,而 Oracle 数据库另外使用 DRCP
with engine.connect() as conn:
print(conn.scalar("select 1 from dual"))
Unicode¶
与 Python 3 下的所有 DBAPI 一样,所有字符串本质上都是 Unicode 字符串。然而,在所有情况下,驱动程序都需要显式的编码配置。
确保正确的客户端编码¶
为几乎所有 Oracle 数据库相关软件建立客户端编码的长期公认标准是通过 NLS_LANG 环境变量。旧版本的 cx_Oracle 使用此环境变量作为其编码配置的来源。此变量的格式为 Territory_Country.CharacterSet;一个典型的值是 AMERICAN_AMERICA.AL32UTF8
。cx_Oracle 版本 8 及更高版本默认使用字符集 “UTF-8”,并忽略 NLS_LANG 的字符集组件。
cx_Oracle 驱动程序还支持一种程序化替代方案,即将 encoding
和 nencoding
参数直接传递给其 .connect()
函数。这些可以存在于 URL 中,如下所示
engine = create_engine(
"oracle+cx_oracle://scott:tiger@tnsalias?encoding=UTF-8&nencoding=UTF-8"
)
有关 encoding
和 nencoding
参数的含义,请查阅 字符集和国家语言支持 (NLS)。
另请参阅
字符集和国家语言支持 (NLS) - 在 cx_Oracle 文档中。
Unicode 特定列数据类型¶
Core 表达式语言通过使用 Unicode
和 UnicodeText
数据类型来处理 Unicode 数据。默认情况下,这些类型对应于 VARCHAR2 和 CLOB Oracle 数据库数据类型。当将这些数据类型与 Unicode 数据一起使用时,预计数据库已配置为 Unicode 感知的字符集,并且 NLS_LANG
环境变量已正确设置(这适用于旧版本的 cx_Oracle),以便 VARCHAR2 和 CLOB 数据类型可以容纳数据。
如果 Oracle 数据库未配置 Unicode 字符集,则有两个选项:显式使用 NCHAR
和 NCLOB
数据类型,或者将标志 use_nchar_for_unicode=True
传递给 create_engine()
,这将导致 SQLAlchemy 方言对 Unicode
/ UnicodeText
数据类型使用 NCHAR/NCLOB,而不是 VARCHAR/CLOB。
版本 1.3 中变更:Unicode
和 UnicodeText
数据类型现在对应于 VARCHAR2
和 CLOB
Oracle 数据库数据类型,除非在调用 create_engine()
时,将 use_nchar_for_unicode=True
传递给方言。
编码错误¶
对于 Oracle 数据库中出现编码损坏数据的异常情况,方言接受一个参数 encoding_errors
,该参数将传递给 Unicode 解码函数,以影响如何处理解码错误。该值最终由 Python 的 decode 函数使用,并通过 cx_Oracle 的 encodingErrors
参数(由 Cursor.var()
使用)以及 SQLAlchemy 自己的解码函数传递,因为 cx_Oracle 方言在不同情况下都会使用这两者。
版本 1.3.11 中的新功能。
使用 setinputsizes 对 cx_Oracle 数据绑定性能进行细粒度控制¶
cx_Oracle DBAPI 深度且根本地依赖于 DBAPI setinputsizes()
调用的使用。此调用的目的是为作为参数传递的 Python 值建立绑定到 SQL 语句的数据类型。虽然几乎没有其他 DBAPI 为 setinputsizes()
调用分配任何用途,但 cx_Oracle DBAPI 在其与 Oracle 数据库客户端接口的交互中严重依赖它,并且在某些情况下,SQLAlchemy 无法确切知道应如何绑定数据,因为某些设置可能会导致截然不同的性能特征,同时改变类型强制行为。
强烈建议 cx_Oracle 方言的用户阅读 cx_Oracle 在 https://cx-oracle.readthedocs.io/en/latest/api_manual/module.html#database-types 中内置数据类型符号列表。请注意,在某些情况下,使用这些类型与不使用这些类型相比,可能会发生显著的性能下降,尤其是在指定 cx_Oracle.CLOB
时。
在 SQLAlchemy 方面,DialectEvents.do_setinputsizes()
事件既可以用于运行时可见性(例如,日志记录)setinputsizes 步骤,也可以完全控制如何在每个语句的基础上使用 setinputsizes()
。
版本 1.2.9 中的新功能:添加了 DialectEvents.setinputsizes()
示例 1 - 记录所有 setinputsizes 调用¶
以下示例说明了如何从 SQLAlchemy 的角度记录中间值,然后再将其转换为原始 setinputsizes()
参数字典。字典的键是 BindParameter
对象,这些对象具有 .key
和 .type
属性
from sqlalchemy import create_engine, event
engine = create_engine("oracle+cx_oracle://scott:tiger@host/xe")
@event.listens_for(engine, "do_setinputsizes")
def _log_setinputsizes(inputsizes, cursor, statement, parameters, context):
for bindparam, dbapitype in inputsizes.items():
log.info(
"Bound parameter name: %s SQLAlchemy type: %r DBAPI object: %s",
bindparam.key,
bindparam.type,
dbapitype,
)
示例 2 - 删除所有 CLOB 的绑定¶
cx_Oracle 中的 CLOB
数据类型会产生显著的性能开销,但是 SQLAlchemy 1.2 系列中 Text
类型默认设置为此数据类型。此设置可以按如下方式修改
from sqlalchemy import create_engine, event
from cx_Oracle import CLOB
engine = create_engine("oracle+cx_oracle://scott:tiger@host/xe")
@event.listens_for(engine, "do_setinputsizes")
def _remove_clob(inputsizes, cursor, statement, parameters, context):
for bindparam, dbapitype in list(inputsizes.items()):
if dbapitype is CLOB:
del inputsizes[bindparam]
LOB 数据类型¶
LOB 数据类型是指 “大对象” 数据类型,例如 CLOB、NCLOB 和 BLOB。现代版本的 cx_Oracle 针对将这些数据类型作为单个缓冲区传递进行了优化。因此,SQLAlchemy 默认使用这些较新的类型处理程序。
要禁用较新的类型处理程序的使用,并将 LOB 对象作为具有 read()
方法的经典缓冲对象传递,可以将参数 auto_convert_lobs=False
传递给 create_engine()
,这仅在引擎范围内生效。
RETURNING 支持¶
cx_Oracle 方言使用 OUT 参数实现 RETURNING。该方言完全支持 RETURNING。
不支持两阶段事务¶
由于驱动程序支持不佳,不支持 cx_Oracle 下的两阶段事务。但是,较新的 python-oracledb 方言确实支持两阶段事务。
精确数值¶
SQLAlchemy 的数值类型可以处理接收和返回作为 Python Decimal
对象或浮点数对象的值。当使用 Numeric
对象或子类(例如 Float
、DOUBLE_PRECISION
等)时,Numeric.asdecimal
标志确定是否应在返回时将值强制转换为 Decimal
,还是作为浮点数对象返回。为了使情况在 Oracle 数据库下更加复杂,如果 “scale” 为零,则 NUMBER
类型也可以表示整数值,因此 Oracle 数据库特定的 NUMBER
类型也考虑到了这一点。
cx_Oracle 方言广泛使用连接级和游标级 “outputtypehandler” 可调用对象,以便根据请求强制转换数值。这些可调用对象特定于正在使用的 Numeric
的特定风格,以及是否没有 SQLAlchemy 类型对象。在某些观察到的场景中,Oracle 数据库可能会发送关于返回的数值类型的不完整或不明确的信息,例如查询中的数值类型埋藏在多个子查询级别之下。类型处理程序尽最大努力在所有情况下做出正确的决定,并将所有驱动程序可以做出最佳决定的情况推迟到基础的 cx_Oracle DBAPI。
当没有类型对象时,例如在执行纯 SQL 字符串时,会存在一个默认的 “outputtypehandler”,它通常会将指定精度和比例的数值作为 Python Decimal
对象返回。为了性能原因禁用此强制转换为十进制,请将标志 coerce_to_decimal=False
传递给 create_engine()
engine = create_engine("oracle+cx_oracle://dsn", coerce_to_decimal=False)
coerce_to_decimal
标志仅影响未与 Numeric
SQLAlchemy 类型(或此类类型的子类)关联的纯字符串 SQL 语句的结果。
版本 1.2 中变更:cx_Oracle 的数值处理系统已经过重新设计,以利用更新的 cx_Oracle 功能以及更好地集成 outputtypehandlers。
flambé! 龙和 The Alchemist 图像设计由 Rotem Yaari 创建并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档最后生成时间:Tue 11 Mar 2025 02:40:17 PM EDT