SqlAlchemy-2-0-中文文档-三十一-

SqlAlchemy 2.0 中文文档(三十一)

原文:docs.sqlalchemy.org/en/20/contents.html

Oracle

docs.sqlalchemy.org/en/20/dialects/oracle.html

支持 Oracle 数据库。

下表总结了数据库发布版本的当前支持级别。

支持的 Oracle 版本

支持类型 版本
CI 完全测试通过 18c
正常支持 11+
尽力而为 9+

DBAPI 支持

下列方言/DBAPI 选项可用。请参考各个 DBAPI 部分获取连接信息。

  • cx-Oracle

  • python-oracledb

自增行为

包含整数主键的 SQLAlchemy Table 对象通常被假定具有“自动递增”行为,这意味着它们可以在插入时生成自己的主键值。在 Oracle 中,有两种可用的选项,即使用 IDENTITY 列(仅限 Oracle 12 及以上版本)或将 SEQUENCE 与列关联。

指定 GENERATED AS IDENTITY(Oracle 12 及以上)

从版本 12 开始,Oracle 可以使用 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,以指定在与“BY DEFAULT”标识列一起使用时的 ON NULL。

使用 SEQUENCE(所有 Oracle 版本)

旧版 Oracle 没有“自动递增”功能,SQLAlchemy 依赖序列来生成这些值。对于旧版 Oracle,必须始终明确指定序列以启用自动递增。这与大多数文档示例不一致,后者假定使用支持自动递增的数据库。要指定序列,请使用传递给 Column 构造函数的 sqlalchemy.schema.Sequence 对象:

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
)

从版本 1.4 起更改:在 Column 中添加了 Identity 构造,用于指定自增列的选项。

事务隔离级别 / 自动提交

Oracle 数据库支持“READ COMMITTED”和“SERIALIZABLE”隔离模式。cx_Oracle 方言还支持 AUTOCOMMIT 隔离级别。

若要使用每个连接的执行选项进行设置:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="AUTOCOMMIT"
)

对于 READ COMMITTEDSERIALIZABLE,Oracle 方言使用 ALTER SESSION 在会话级别设置级别,在连接返回到连接池时会恢复到默认设置。

isolation_level 的有效值包括:

  • READ COMMITTED

  • AUTOCOMMIT

  • SERIALIZABLE

注意

Oracle 方言实现的 Connection.get_isolation_level() 方法必要地使用 Oracle LOCAL_TRANSACTION_ID 函数启动事务;否则通常无法读取任何级别。

此外,如果由于权限或其他原因导致 v$transaction 视图不可用,Connection.get_isolation_level() 方法将引发异常,这在 Oracle 安装中是常见的。

当方言首次连接到数据库时,cx_Oracle 方言尝试调用 Connection.get_isolation_level() 方法以获取“默认”隔离级别。这个默认级别是必要的,以便在使用 Connection.execution_options() 方法临时修改连接后,可以将级别重置为连接。在常见事件中,Connection.get_isolation_level() 方法由于 v$transaction 不可读以及任何其他与数据库相关的故障而引发异常时,级别被假定为“READ COMMITTED”。对于这种初始首次连接条件,不会发出警告,因为预计这是 Oracle 数据库的常见限制。

版本 1.3.16 中新增了对 cx_oracle 方言的 AUTOCOMMIT 支持,以及默认隔离级别的概念

版本 1.3.21 中新增了对 SERIALIZABLE 的支持,以及隔离级别的实时读取。

版本 1.3.22 中的更改:在默认隔离级别由于 v$transaction 视图的权限而无法读取的情况下(这在 Oracle 安装中很常见),默认隔离级别被硬编码为“READ COMMITTED”,这是 1.3.21 之前的行为。

请参阅

设置事务隔离级别,包括 DBAPI 自动提交

标识符大小写

在 Oracle 中,数据字典使用大写文本表示所有不区分大小写的标识符名称。另一方面,SQLAlchemy 将所有小写标识符名称视为不区分大小写。Oracle 方言在模式级通信(如表和索引的反射)期间将所有不区分大小写的标识符转换为这两种格式之一。在 SQLAlchemy 方面使用大写名称表示区分大小写的标识符,SQLAlchemy 将引用该名称 - 这将导致与从 Oracle 收到的数据字典数据不匹配,因此除非标识符名称真正被创建为区分大小写(即使用带引号的名称),否则在 SQLAlchemy 方面应使用所有小写名称。

最大标识符长度

截至 Oracle Server 版本 12.2,Oracle 已更改了默认的最大标识符长度。在此版本之前,长度为 30,在 12.2 及更高版本中,现在为 128。这一变化影响了 SQLAlchemy 在生成的 SQL 标签名称以及约束名称的区域,特别是在使用描述在 配置约束命名约定 中的约束命名约定特性时。

为了辅助这一变化和其他变化,Oracle 包括了“兼容性”版本的概念,这是一个与实际服务器版本无关的版本号,用于帮助迁移 Oracle 数据库,并可以在 Oracle 服务器内部配置。这个兼容性版本是使用查询 SELECT value FROM v$parameter WHERE name = 'compatible'; 检索的。当 SQLAlchemy Oracle 方言被要求确定默认的最大标识符长度时,它将在第一次连接时尝试使用此查询,以确定服务器的有效兼容性版本,该版本确定了服务器允许的最大标识符长度。如果表不可用,则使用服务器版本信息。

从 SQLAlchemy 1.4 开始,Oracle 方言的默认最大标识符长度为 128 个字符。在第一次连接时,检测到兼容性版本,如果小于 Oracle 版本 12.2,则将最大标识符长度更改为 30 个字符。在所有情况下,设置 create_engine.max_identifier_length 参数将绕过此更改,并且将使用给定的值:

engine = create_engine(
    "oracle+cx_oracle://scott:tiger@oracle122",
    max_identifier_length=30)

最大标识符长度在生成 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 时,它变成:

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 之前版本的应用程序因此可能受到以下情景的影响:希望对以较短长度生成的名称进行“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()

通过在任何 Oracle 版本上传递 enable_offset_fetch=Falsecreate_engine(),可以禁用 FETCH FIRST / OFFSET 的使用,这将强制使用使用窗口函数的“传统”模式。在使用 Oracle 12c 之前的版本时,也会自动选择此模式。

在使用传统模式或将带有限制/偏移的 Select 语句嵌入到复合语句中时,将使用基于窗口函数的 LIMIT / OFFSET 的模拟方法,涉及使用 ROW_NUMBER 创建子查询,这种方法容易出现性能问题以及对于复杂语句的 SQL 构建问题。但是,所有 Oracle 版本都支持此方法。请参阅下面的说明。

关于 LIMIT / OFFSET 模拟(当无法使用 fetch() 方法时)

如果在 Oracle 12c 之前的版本中使用 Select.limit()Select.offset(),或使用 ORM 中的 Query.limit()Query.offset() 方法,以下注意事项适用:

  • SQLAlchemy 目前使用 ROWNUM 来实现 LIMIT/OFFSET;确切的方法取自 blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results

  • 默认情况下不使用“FIRST_ROWS()”优化关键字。要启用此优化指令的使用,请在 create_engine() 中指定 optimize_limits=True

    1.4 版更改:Oracle 方言使用“编译后”方案呈现限制/偏移整数值,直接在传递语句给游标执行之前呈现整数。use_binds_for_limits 标志不再起作用。

    另请参阅

    Oracle 中用于 LIMIT/OFFSET 的新“编译后”绑定参数,以及 SQL Server。

RETURNING 支持

Oracle 数据库完全支持对使用单个绑定参数集合调用的 INSERT、UPDATE 和 DELETE 语句进行 RETURNING(即cursor.execute()风格语句;SQLAlchemy 通常不支持在 executemany 语句中使用 RETURNING)。也可以返回多行。

2.0 版更改:Oracle 后端具有与其他后端相同的 RETURNING 完全支持。

ON UPDATE CASCADE

Oracle 没有本地的 ON UPDATE CASCADE 功能。可以在 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 时,方言会自动配置为以下行为:

  • 使用 use_ansi 标志设置为 False。这会将所有 JOIN 词组转换为 WHERE 子句,并且在左外连接的情况下使用 Oracle 的 (+) 运算符。

  • 当使用 Unicode 时,NVARCHAR2 和 NCLOB 数据类型不再生成 DDL - 而是生成 VARCHAR2 和 CLOB。这是因为即使这些类型可用,它们在 Oracle 8 上似乎无法正常工作。NVARCHARNCLOB 类型将始终生成 NVARCHAR2 和 NCLOB。

在使用反射与表对象时,方言可以选择性地搜索由同义词指示的表,可以是在本地或远程模式或通过 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 方言现在可以反映唯一约束和检查约束。

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+cx_oracle://s:t@dsn")
    inspector = inspect(engine)
    all_check_constraints = inspector.get_check_constraints(
        "some_table", include_all=True)
    
  • 在大多数情况下,当反映一个Table时,唯一约束将作为UniqueConstraint对象可用,因为在大多数情况下,Oracle 使用唯一索引来镜像唯一约束(例外情况似乎是当两个或更多个唯一约束表示相同的列时);Table将使用设置了unique=True标志的Index来代替这些。

  • Oracle 为表的主键创建一个隐式索引;此索引被排除在所有索引结果之外。

  • 对于索引反映的列列表,不会包括以 SYS_NC 开头的列名。

具有 SYSTEM/SYSAUX 表空间的表名称

Inspector.get_table_names()Inspector.get_temp_table_names() 方法分别返回当前引擎的表名列表。这些方法也是在操作中进行反射的一部分,例如MetaData.reflect()。默认情况下,这些操作将从操作中排除SYSTEMSYSAUX表空间。为了更改此设置,可以在引擎级别使用exclude_tablespaces参数更改默认的排除表空间列表:

# exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM
e = create_engine(
  "oracle+cx_oracle://scott:tiger@xe",
  exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"])

日期时间兼容性

Oracle 没有名为DATETIME的数据类型,它只有DATE,实际上可以存储日期和时间值。因此,Oracle 方言提供了一个DATE类型,它是DateTime的子类。此类型没有特殊行为,仅作为此类型的“标记”存在;此外,当反映数据库列并且类型报告为DATE时,将使用支持时间的DATE类型。

Oracle 表选项

在与Table结构一起使用 Oracle 时,CREATE 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)
    
    The ``oracle_compress`` parameter accepts either an integer compression
    level, or ``True`` to use the default compression level.
    ```  ## 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 的构造参数的类型如下:

对象名称 描述
BFILE
BINARY_DOUBLE
BINARY_FLOAT
DATE 提供 oracle DATE 类型。
FLOAT Oracle FLOAT。
INTERVAL
LONG
NCLOB
NUMBER
NVARCHAR2 NVARCHAR 的别名
RAW
ROWID Oracle ROWID 类型。
TIMESTAMP Oracle 实现的TIMESTAMP,支持额外的 Oracle 特定模式
class sqlalchemy.dialects.oracle.BFILE

成员

init()

类签名

sqlalchemy.dialects.oracle.BFILE (sqlalchemy.types.LargeBinary)

method __init__(length: int | None = None)

LargeBinarysqlalchemy.types.LargeBinary.__init__ 方法继承

构造一个 LargeBinary 类型。

参数:

length – 可选,用于 DDL 语句中的列长度,适用于接受长度的二进制类型,例如 MySQL 的 BLOB 类型。

class sqlalchemy.dialects.oracle.BINARY_DOUBLE

成员

init()

类签名

sqlalchemy.dialects.oracle.BINARY_DOUBLE (sqlalchemy.types.Double)

method __init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)

Floatsqlalchemy.types.Float.__init__ 方法继承

构造一个 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 的默认值。

class sqlalchemy.dialects.oracle.BINARY_FLOAT

成员

init()

类签名

sqlalchemy.dialects.oracle.BINARY_FLOAT (sqlalchemy.types.Float)

method __init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)

继承自 Float sqlalchemy.types.Float.__init__ 方法

构造一个 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 的默认值。

class sqlalchemy.dialects.oracle.DATE

提供 Oracle DATE 类型。

此类型没有特殊的 Python 行为,除了它是DateTime的子类;这是为了适应 Oracle DATE 类型支持时间值的事实。

成员

init()

类签名

sqlalchemy.dialects.oracle.DATEsqlalchemy.dialects.oracle.types._OracleDateLiteralRendersqlalchemy.types.DateTime

method __init__(timezone: bool = False)

继承自 DateTime sqlalchemy.types.DateTime.__init__ 方法

构造一个新的DateTime

参数:

时区 – 布尔值。表示日期时间类型是否应在仅基本日期/时间保存类型上启用时区支持(如果可用)。建议在使用此标志时直接使用TIMESTAMP数据类型,因为一些数据库包括与时区可用的 TIMESTAMP 数据类型不同的独立通用日期/时间保存类型,例如 Oracle。

class sqlalchemy.dialects.oracle.FLOAT

Oracle FLOAT。

这与FLOAT相同,不同之处在于接受特定于 Oracle 的FLOAT.binary_precision参数,并且不接受Float.precision参数。

Oracle FLOAT 类型以“二进制精度”表示精度,默认为 126。对于 REAL 类型,该值为 63。此参数不清晰地映射到特定数量的小数位数,但大致相当于所需小数位数除以 0.3103。

2.0 版中的新功能。

成员

init()

类签名

class sqlalchemy.dialects.oracle.FLOAT (sqlalchemy.types.FLOAT)

method __init__(binary_precision=None, asdecimal=False, decimal_return_scale=None)

构造一个 FLOAT

参数:

  • binary_precision – 要在 DDL 中呈现的 Oracle 二进制精度值。这可以使用公式“十进制精度= 0.30103 * 二进制精度”来近似到十进制字符的数量。Oracle 用于 FLOAT / DOUBLE PRECISION 的默认值为 126。

  • asdecimal – 参见Float.asdecimal

  • decimal_return_scale – 参见Float.decimal_return_scale

class sqlalchemy.dialects.oracle.INTERVAL

成员

init()

类签名

class sqlalchemy.dialects.oracle.INTERVAL (sqlalchemy.types.NativeForEmulated, sqlalchemy.types._AbstractInterval)

method __init__(day_precision=None, second_precision=None)

构造一个 INTERVAL。

请注意,当前仅支持 DAY TO SECOND 间隔。这是由于可用 DBAPI 中缺少对 YEAR TO MONTH 间隔的支持。

参数:

  • day_precision – 日期精度值。这是要存储的日字段的位数。默认为“2”。

  • second_precision – 秒精度值。这是要存储的分数秒字段的位数。默认为“6”。

class sqlalchemy.dialects.oracle.NCLOB

成员

init()

类签名

sqlalchemy.dialects.oracle.NCLOB类(sqlalchemy.types.Text

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选的,在 DDL 和 CAST 表达式中使用的列的长度。如果不会发出CREATE TABLE,可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

attribute sqlalchemy.dialects.oracle.NVARCHAR2

NVARCHAR的别名

class sqlalchemy.dialects.oracle.NUMBER

类签名

sqlalchemy.dialects.oracle.NUMBER类(sqlalchemy.types.Numericsqlalchemy.types.Integer

class sqlalchemy.dialects.oracle.LONG

成员

init()

类签名

sqlalchemy.dialects.oracle.LONG类(sqlalchemy.types.Text

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选的,在 DDL 和 CAST 表达式中使用的列的长度。如果不会发出CREATE TABLE,可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来表示预期存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.oracle.RAW

类签名

sqlalchemy.dialects.oracle.RAW (sqlalchemy.types._Binary)

class sqlalchemy.dialects.oracle.ROWID

Oracle ROWID 类型。

在 cast() 或类似情况下使用时,生成 ROWID。

类签名

sqlalchemy.dialects.oracle.ROWID (sqlalchemy.types.TypeEngine)

class sqlalchemy.dialects.oracle.TIMESTAMP

Oracle 实现的 TIMESTAMP,支持额外的 Oracle 特定模式

2.0 版本中的新功能。

成员

init()

类签名

sqlalchemy.dialects.oracle.TIMESTAMP (sqlalchemy.types.TIMESTAMP)

method __init__(timezone: bool = False, local_timezone: bool = False)

构造一个新的TIMESTAMP

参数:

  • timezone – 布尔值。表示 TIMESTAMP 类型应该使用 Oracle 的 TIMESTAMP WITH TIME ZONE 数据类型。

  • local_timezone – 布尔值。表示 TIMESTAMP 类型应该使用 Oracle 的 TIMESTAMP WITH LOCAL TIME ZONE 数据类型。

cx_Oracle

通过 cx-Oracle 驱动程序支持 Oracle 数据库。

DBAPI

cx-Oracle 的文档和下载信息(如果适用)可在此处获得:oracle.github.io/python-cx_Oracle/

连接

连接字符串:

oracle+cx_oracle://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

DSN vs. 主机名连接

cx_Oracle 提供了几种指示目标数据库的方法。方言从一系列不同的 URL 形式转换而来。

使用 Easy Connect 语法的主机名连接

给定目标 Oracle 数据库的主机名、端口和服务名称,例如来自 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")

不支持完整的 Easy Connect 语法。而是使用 tnsnames.ora 文件,并使用 DSN 进行连接。

带有 tnsnames.ora 或 Oracle Cloud 的连接

或者,如果没有提供端口、数据库名称或 service_name,方言将使用 Oracle DSN “连接字符串”。这将 URL 的“主机名”部分视为数据源名称。例如,如果 tnsnames.ora 文件包含如下的网络服务名称 myalias

myalias =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orclpdb1)
    )
  )

myalias 是 URL 的主机名部分,而没有指定端口、数据库名称或 service_name 时,cx_Oracle 方言将连接到此数据库服务:

engine = create_engine("oracle+cx_oracle://scott:tiger@myalias/?encoding=UTF-8&nencoding=UTF-8")

Oracle Cloud 的用户应该使用这种语法,并按照 cx_Oracle 文档 连接到 Autonomous 数据库 中所示配置云钱包。

SID 连接

要使用 Oracle 的过时 SID 连接语法,SID 可以像下面这样通过 URL 的“数据库名称”部分传递:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:1521/dbname?encoding=UTF-8&nencoding=UTF-8")

上面,传递给 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)))'

传递 cx_Oracle 连接参数

通常可以通过 URL 查询字符串传递其他连接参数;特定符号如 cx_Oracle.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 中,encodingnencoding 的默认值已更改为 “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 方言接受的参数如下:

  • arraysize - 在游标上设置 cx_oracle.arraysize 的值;默认为 None,表示应该使用驱动程序的默认值(通常值为 100)。此设置控制在获取行时缓冲多少行,并且在修改时可以对性能产生重大影响。该设置用于 cx_Oracle 以及 oracledb

    在版本 2.0.26 中更改:- 将默认值从 50 更改为 None,以使用驱动程序本身的默认值。

  • auto_convert_lobs - 默认为 True;详见 LOB 数据类型。

  • coerce_to_decimal - 详见精度数值。

  • encoding_errors - 详见编码错误。

使用 cx_Oracle 会话池

cx_Oracle 库提供了自己的连接池实现,可以替代 SQLAlchemy 的池功能。可以通过使用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=2, max=5, 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 和应用程序连续性

使用 Oracle 数据库 Resident 连接池(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 像大多数其他 Oracle 驱动程序一样将使用此环境变量作为其编码配置的来源。此变量的格式是特殊的;典型的值可能是AMERICAN_AMERICA.AL32UTF8

cx_Oracle 驱动程序还支持一种编程方式,即直接将encodingnencoding参数传递给其.connect()函数。这些可以在 URL 中如下所示:

engine = create_engine("oracle+cx_oracle://scott:tiger@orclpdb/?encoding=UTF-8&nencoding=UTF-8")

关于encodingnencoding参数的含义,请参考字符集和国家语言支持(NLS)

另见

字符集和国家语言支持(NLS) - 在 cx_Oracle 文档中。

Unicode 特定的列数据类型

核心表达式语言通过使用UnicodeUnicodeText 数据类型处理 Unicode 数据。这些类型默认对应于 VARCHAR2 和 CLOB Oracle 数据类型。在使用这些数据类型处理 Unicode 数据时,预期 Oracle 数据库配置为具有 Unicode 意识的字符集,并且NLS_LANG环境变量设置正确,以便 VARCHAR2 和 CLOB 数据类型可以容纳数据。

如果 Oracle 数据库未配置为 Unicode 字符集,则有两种选择:显式使用NCHARNCLOB数据类型,或者在调用create_engine()时传递标志use_nchar_for_unicode=True,这将导致 SQLAlchemy 方言使用 NCHAR/NCLOB 代替 VARCHAR/CLOB 用于Unicode / UnicodeText 数据类型。

从版本 1.3 开始更改:UnicodeUnicodeText 数据类型现在对应于 VARCHAR2CLOB 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 的内置数据类型符号列表,网址为 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 调用

以下示例说明了如何在将其转换为原始 setinputsizes() 参数字典之前从 SQLAlchemy 视角记录中间值。字典的键是具有 .key.type 属性的 BindParameter 对象:

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]
```  ### RETURNING 支持

cx_Oracle 方言使用 OUT 参数实现 RETURNING。该方言完全支持 RETURNING。  ### LOB 数据类型

LOB 数据类型是指诸如 CLOB、NCLOB 和 BLOB 之类的“大对象”数据类型。cx_Oracle 和 oracledb 的现代版本经过优化,可以将这些数据类型作为单个缓冲区传递。因此,SQLAlchemy 默认使用这些新型处理程序。

要禁用新型类型处理程序的使用,并将 LOB 对象作为具有 `read()` 方法的经典缓冲对象传递,可以向 `create_engine()` 传递参数 `auto_convert_lobs=False`,这仅在引擎范围内生效。

### 不支持两阶段事务

由于驱动程序支持不佳,cx_Oracle 不支持两阶段事务。从 cx_Oracle 6.0b1 开始,两阶段事务的接口已更改为更直接地通过到底层 OCI 层的传递,自动化程度较低。支持此系统的附加逻辑未在 SQLAlchemy 中实现。

### 精确数字

SQLAlchemy 的数字类型可以处理接收和返回 Python `Decimal` 对象或浮点对象的值。当使用 `Numeric` 对象或其子类如 `Float`,`DOUBLE_PRECISION` 等时,`Numeric.asdecimal` 标志确定返回时值是否应强制转换为 `Decimal`,或返回为浮点对象。在 Oracle 下更加复杂,Oracle 的 `NUMBER` 类型如果“scale”为零,也可以表示整数值,因此 Oracle 特定的 `NUMBER` 类型也考虑到了这一点。

cx_Oracle 方言广泛使用连接和游标级别的“outputtypehandler”可调用对象,以根据请求强制转换数值。这些可调用对象特定于使用的 `Numeric` 的具体类型,以及如果不存在 SQLAlchemy 类型对象。观察到的情况是,Oracle 可能发送关于返回的数字类型的不完整或模糊信息,例如查询中数字类型被埋在多层子查询中。类型处理程序在所有情况下都尽力做出正确决定,对于所有那些驱动程序可以做出最佳决定的情况,都会推迟到底层的 cx_Oracle DBAPI。

当不存在类型对象时,例如执行普通 SQL 字符串时,存在默认的“outputtypehandler”,通常会返回指定精度和标度的数值,作为 Python `Decimal` 对象。为了出于性能原因禁用此强制转换为十进制数的操作,请将标志 `coerce_to_decimal=False` 传递给 `create_engine()`:

```py
engine = create_engine("oracle+cx_oracle://dsn", coerce_to_decimal=False)

coerce_to_decimal 标志仅影响与 Numeric SQLAlchemy 类型(或其子类)无关的普通字符串 SQL 语句的结果。

从版本 1.2 开始更改:cx_Oracle 的数字处理系统已经重新设计,以利用更新的 cx_Oracle 功能以及更好地集成 outputtypehandlers。 ## python-oracledb

通过 python-oracledb 驱动程序支持 Oracle 数据库。

DBAPI

python-oracledb 的文档和下载信息(如果适用)可在此处获取:oracle.github.io/python-oracledb/

连接

连接字符串:

oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

python-oracledb 是由 Oracle 发布的用于取代 cx_Oracle 驱动程序的驱动程序。它与 cx_Oracle 完全兼容,并且具有“thin”客户端模式(不需要依赖项)和“thick”模式(与 cx_Oracle 一样使用 Oracle 客户端接口)。

另请参阅

cx_Oracle - cx_Oracle 的所有说明也适用于 oracledb 驱动程序。

SQLAlchemy oracledb 方言在同一方言名称下提供了同步和异步实现。根据引擎的创建方式选择适当的版本:

  • 使用 oracle+oracledb://... 调用 create_engine() 将自动选择同步版本,例如:

    from sqlalchemy import create_engine
    sync_engine = create_engine("oracle+oracledb://scott:tiger@localhost/?service_name=XEPDB1")
    
  • 使用 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=XEPDB1")
    

也可以明确指定方言的 asyncio 版本,使用 oracledb_async 后缀,如:

from sqlalchemy.ext.asyncio import create_async_engine
asyncio_engine = create_async_engine("oracle+oracledb_async://scott:tiger@localhost/?service_name=XEPDB1")

2.0.25 版本中的新功能:增加了对 oracledb 的异步版本的支持。

Thick 模式支持

默认情况下,python-oracledb 以 thin 模式启动,不需要在系统中安装 Oracle 客户端库。python-oracledb 驱动程序还支持“thick”模式,行为类似于 cx_oracle,需要安装 Oracle 客户端接口(OCI)。

要启用此模式,用户可以手动调用 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", "driver_name": "my-app"
})

另请参阅

python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.init_oracle_client

2.0.0 版本中的新功能:增加了对 oracledb 驱动程序的支持。

对 Oracle 数据库的支持。

以下表格总结了当前数据库发布版本的支持级别。

支持的 Oracle 版本

支持类型 版本
在 CI 中进行完整测试 18c
普通支持 11+
尽力而为 9+

DBAPI 支持

提供以下方言/DBAPI 选项。请参阅各自的 DBAPI 部分获取连接信息。

  • cx-Oracle

  • python-oracledb

自动增量行为

包括整数主键的 SQLAlchemy Table 对象通常被假定具有“自动增量”行为,意味着它们可以在插入时生成自己的主键值。在 Oracle 中,有两个可用选项,即使用 IDENTITY 列(仅限 Oracle 12 及以上版本)或将序列与列相关联。

指定 GENERATED AS IDENTITY(Oracle 12 及以上)

从版本 12 开始,Oracle 可以使用 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,以指定在“BY DEFAULT”身份列上与“ON NULL”一起使用。

使用 SEQUENCE(所有 Oracle 版本)

早期版本的 Oracle 没有“autoincrement”功能,SQLAlchemy 依赖序列来生成这些值。对于旧版的 Oracle,必须始终明确指定序列以启用自动增量。这与大多数文档示例不同,后者假设使用的是具有自动增量功能的数据库。要指定序列,请使用传递给列构造函数的 sqlalchemy.schema.Sequence 对象:

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
)

版本 1.4 中的更改:在 Column 中添加了 Identity 构造函数,用于指定自动增量列的选项。

指定 GENERATED AS IDENTITY(Oracle 12 及以上)

从版本 12 开始,Oracle 可以使用 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,以使用默认生成模式,将 GENERATED AS IDENTITY 渲染到 DDL 中。它还支持将 Identity.on_null 设置为 True,以指定与 “BY DEFAULT” 身份列一起使用 ON NULL。

使用 SEQUENCE(所有 Oracle 版本)

旧版本的 Oracle 没有“自动增量”功能,SQLAlchemy 依赖序列来生成这些值。在旧的 Oracle 版本中,必须始终明确指定序列以启用自动增量。这与大多数文档示例不同,后者假定使用支持自动增量的数据库。要指定序列,请使用传递给 Column 结构的 sqlalchemy.schema.Sequence 对象:

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
)

从 1.4 版本开始更改:在 Column 中添加 Identity 结构以指定自动增量列的选项。

事务隔离级别 / 自动提交

Oracle 数据库支持“READ COMMITTED”和“SERIALIZABLE”隔离模式。 cx_Oracle 方言也支持 AUTOCOMMIT 隔离级别。

设置使用每个连接的执行选项:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="AUTOCOMMIT"
)

对于 READ COMMITTEDSERIALIZABLE,Oracle 方言使用 ALTER SESSION 在会话级别设置级别,当连接返回到连接池时,它将恢复为其默认设置。

isolation_level 的有效值包括:

  • READ COMMITTED

  • AUTOCOMMIT

  • SERIALIZABLE

注意

由 Oracle 方言实现的 Connection.get_isolation_level() 方法的实现必然使用 Oracle LOCAL_TRANSACTION_ID 函数启动事务;否则,通常不可读取级别。

此外,如果由于权限或其他原因导致 v$transaction 视图不可用,Connection.get_isolation_level() 方法将引发异常,在 Oracle 安装中这是常见的情况。

当 cx_Oracle 方言在其首次连接到数据库时,会尝试调用Connection.get_isolation_level()方法,以获取“默认”隔离级别。这个默认级别是必需的,以便在使用Connection.execution_options()方法临时修改连接后,可以重置级别。在常见情况下,如果Connection.get_isolation_level()方法由于v$transaction不可读以及任何其他与数据库相关的故障而引发异常,则假定级别为“READ COMMITTED”。对于这种初始第一次连接条件,不会发出警告,因为预计这是 Oracle 数据库上的常见限制。

自版本 1.3.16 新增:为 cx_oracle 方言添加了对 AUTOCOMMIT 的支持以及默认隔离级别的概念

自版本 1.3.21 新增:增加了对 SERIALIZABLE 的支持以及隔离级别的实时读取。

从版本 1.3.22 起更改:如果由于在 Oracle 安装中常见的 v$transaction 视图上的权限问题而无法读取默认隔离级别,则默认隔离级别硬编码为“READ COMMITTED”,这是 1.3.21 之前的行为。

另请参阅

设置事务隔离级别,包括 DBAPI 自动提交

标识符大小写

在 Oracle 中,数据字典使用大写文本表示所有不区分大小写的标识符名称。另一方面,SQLAlchemy 认为所有小写标识符名称都是不区分大小写的。Oracle 方言在模式级别通信期间(例如反射表和索引)将所有不区分大小写的标识符转换为这两种格式。在 SQLAlchemy 一侧使用大写名称表示区分大小写的标识符,并且 SQLAlchemy 会对名称加引号 - 这将导致与从 Oracle 接收到的数据字典数据不匹配,因此除非标识符名称真的已创建为区分大小写的(即使用带引号的名称),否则在 SQLAlchemy 一侧应使用所有小写名称。

最大标识符长度

Oracle 在 Oracle Server 版本 12.2 之后更改了默认的最大标识符长度。在此版本之前,长度为 30,在 12.2 及更高版本中,现在为 128。此更改影响 SQLAlchemy 在生成的 SQL 标签名称以及生成约束名称方面的操作,特别是在使用配置约束命名约定中描述的约束命名约定功能的情况下。

为了帮助进行此更改和其他更改,Oracle 包括“兼容性”版本的概念,这是一个与实际服务器版本无关的版本号,以帮助迁移 Oracle 数据库,并且可以在 Oracle 服务器内部配置。此兼容性版本通过查询SELECT value FROM v$parameter WHERE name = 'compatible';检索。当 SQLAlchemy Oracle 方言被要求确定默认最大标识符长度时,将尝试在首次连接时使用此查询以确定服务器的有效兼容性版本,该版本确定服务器的最大允许标识符长度。如果表不可用,则使用服务器版本信息。

从 SQLAlchemy 1.4 开始,Oracle 方言的默认最大标识符长度为 128 个字符。首次连接时,检测兼容性版本,如果低于 Oracle 版本 12.2,则将最大标识符长度更改为 30 个字符。在所有情况下,设置create_engine.max_identifier_length参数将绕过此更改,并且给定的值将如实使用:

engine = create_engine(
    "oracle+cx_oracle://scott:tiger@oracle122",
    max_identifier_length=30)

最大标识符长度在生成 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 时,变为:

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 方言现在对所有包括 ORM 和传统 Query 内部在内的 Select.limit()Select.offset() 使用中都使用 FETCH FIRST N ROW / OFFSET N ROWS。要强制使用基于窗口函数的传统行为,请在 create_engine() 中指定 enable_offset_fetch=False 方言参数。

通过向 create_engine() 传递 enable_offset_fetch=False,可以在任何 Oracle 版本上禁用 FETCH FIRST / OFFSET 的使用,这将强制使用“传统”模式,即使用窗口函数。当使用 Oracle 版本 12c 之前的版本时,也会自动选择此模式。

在使用传统模式或者将带有 limit/offset 的 Select 语句嵌入到复合语句中时,会使用基于窗口函数的 LIMIT / OFFSET 的模拟方法,这涉及使用 ROW_NUMBER 创建子查询,容易出现性能问题以及对复杂语句的 SQL 构造问题。然而,这种方法受到所有 Oracle 版本的支持。请参阅下面的注意事项。

LIMIT / OFFSET 模拟的注意事项(当无法使用 fetch() 方法时)

如果在 Oracle 版本 12c 之前使用 Select.limit()Select.offset() 方法,或者在 ORM 中使用 Query.limit()Query.offset() 方法,则需要注意以下内容:

  • SQLAlchemy 目前使用 ROWNUM 来实现 LIMIT/OFFSET;确切的方法取自blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results

  • “FIRST_ROWS()”优化关键字默认情况下不使用。要启用此优化指令的使用,请在create_engine()中指定optimize_limits=True

    自版本 1.4 起:Oracle 方言使用“编译后”方案呈现限制/偏移整数值,直接在将语句传递给游标执行之前呈现整数。use_binds_for_limits标志不再起作用。

    另请参阅

    Oracle、SQL Server 中用于 LIMIT/OFFSET 的新“编译后”绑定参数。

LIMIT / OFFSET 仿真注意事项(当无法使用 fetch()方法时)

如果在 Oracle 12c 之前的版本中使用Select.limit()Select.offset(),或者在 ORM 中使用Query.limit()Query.offset()方法,则适用以下注意事项:

  • SQLAlchemy 目前使用 ROWNUM 来实现 LIMIT/OFFSET;确切的方法取自blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results

  • “FIRST_ROWS()”优化关键字默认情况下不使用。要启用此优化指令的使用,请在create_engine()中指定optimize_limits=True

    自版本 1.4 起:Oracle 方言使用“编译后”方案呈现限制/偏移整数值,直接在将语句传递给游标执行之前呈现整数。use_binds_for_limits标志不再起作用。

    另请参阅

    Oracle、SQL Server 中用于 LIMIT/OFFSET 的新“编译后”绑定参数。

RETURNING 支持

Oracle 数据库完全支持对使用单个绑定参数集合(即cursor.execute()风格语句;SQLAlchemy 通常不支持 executemany 语句)调用的 INSERT、UPDATE 和 DELETE 语句的 RETURNING。也可以返回多行。

自版本 2.0 起:Oracle 后端完全支持与其他后端相同的 RETURNING 功能。

ON UPDATE CASCADE

Oracle 没有本机的 ON UPDATE CASCADE 功能。一个基于触发器的解决方案可在 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 时,方言内部会配置为以下行为:

  • use_ansi 标志设置为 False。这会将所有 JOIN 短语转换为 WHERE 子句,并且在 LEFT OUTER JOIN 的情况下使用 Oracle 的 (+) 运算符。

  • 当使用 Unicode 时,不再生成 NVARCHAR2 和 NCLOB 数据类型的 DDL - 而是生成 VARCHAR2 和 CLOB。这是因为即使这些类型在 Oracle 8 上是可用的,但在 Oracle 8 上似乎无法正确工作。NVARCHARNCLOB 类型将始终生成 NVARCHAR2 和 NCLOB。

当使用 Table 对象进行反射时,方言可以选择搜索由同义词指示的表,无论是在本地还是远程模式,还是通过 DBLINK 访问,只需将标志 oracle_resolve_synonyms=True 作为关键字参数传递给 Table 构造函数:

some_table = Table('some_table', autoload_with=some_engine,
                            oracle_resolve_synonyms=True)

当设置了此标志时,将会在 ALL_TABLES 视图中搜索给定的名称(例如上面的 some_table),而且还会在 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 方言现在可以反映唯一约束和检查约束。

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+cx_oracle://s:t@dsn")
    inspector = inspect(engine)
    all_check_constraints = inspector.get_check_constraints(
        "some_table", include_all=True)
    
  • 在大多数情况下,当反映Table时,唯一约束将不可用作为UniqueConstraint对象,因为 Oracle 在大多数情况下使用唯一索引来反映唯一约束(例外情况似乎是当两个或多个唯一约束表示相同列时);相反,Table将使用带有unique=True标志的Index来表示这些约束。

  • Oracle 为表的主键创建一个隐式索引;此索引不包含在所有索引结果中。

  • 反映索引的列列表不会包括以 SYS_NC 开头的列名。

具有 SYSTEM/SYSAUX 表空间的表名称

Inspector.get_table_names()Inspector.get_temp_table_names()方法分别返回当前引擎的表名列表。这些方法也是在操作中发生的反射的一部分,比如MetaData.reflect()。默认情况下,这些操作会排除SYSTEMSYSAUX表空间。要更改这一点,可以在引擎级别使用exclude_tablespaces参数更改默认排除的表空间列表:

# exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM
e = create_engine(
  "oracle+cx_oracle://scott:tiger@xe",
  exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"])

日期时间兼容性

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)
    
    The ``oracle_compress`` parameter accepts either an integer compression
    level, or ``True`` to use the default compression level.
    

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_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 特定构造参数的类型:

对象名称 描述
BFILE
BINARY_DOUBLE
BINARY_FLOAT
DATE 提供 Oracle DATE 类型。
FLOAT Oracle FLOAT 类型。
INTERVAL
LONG
NCLOB
NUMBER
NVARCHAR2 NVARCHAR 的别名
RAW
ROWID Oracle ROWID 类型。
TIMESTAMP TIMESTAMP 的 Oracle 实现,支持额外的 Oracle 特定模式。
class sqlalchemy.dialects.oracle.BFILE

成员

init()

类签名

class sqlalchemy.dialects.oracle.BFILE (sqlalchemy.types.LargeBinary)

method __init__(length: int | None = None)

继承自 LargeBinary sqlalchemy.types.LargeBinary.__init__ 方法

构造一个 LargeBinary 类型。

参数:

length – 可选,用于 DDL 语句中的列长度,适用于接受长度的二进制类型,如 MySQL 的 BLOB 类型。

class sqlalchemy.dialects.oracle.BINARY_DOUBLE

成员

init()

类签名

class sqlalchemy.dialects.oracle.BINARY_DOUBLE (sqlalchemy.types.Double)

method __init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)

继承自 Float sqlalchemy.types.Float.__init__ 方法

构造一个 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 的默认值,如果未另行指定。

class sqlalchemy.dialects.oracle.BINARY_FLOAT

成员

init()

类签名

sqlalchemy.dialects.oracle.BINARY_FLOATsqlalchemy.types.Float

method __init__(precision: int | None = None, asdecimal: bool = False, decimal_return_scale: int | None = None)

Float sqlalchemy.types.Float.__init__ 方法继承而来

构造一个 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 的默认值。

class sqlalchemy.dialects.oracle.DATE

提供 Oracle DATE 类型。

此类型在 Python 中没有特殊的行为,除了它是 DateTime 的子类这是为了适应 Oracle DATE 类型支持时间值的事实。

成员

init()

类签名

class sqlalchemy.dialects.oracle.DATE (sqlalchemy.dialects.oracle.types._OracleDateLiteralRender, sqlalchemy.types.DateTime)

method __init__(timezone: bool = False)

继承自 DateTime 方法的 sqlalchemy.types.DateTime.__init__ 方法

构造一个新的 DateTime

参数:

时区 – 布尔值。指示日期时间类型应在仅在基本日期/时间保持类型上可用时启用时区支持。建议在使用此标志时直接使用TIMESTAMP数据类型,因为某些数据库包括与时区支持 TIMESTAMP 数据类型不同的通用日期/时间保持类型,如 Oracle。

class sqlalchemy.dialects.oracle.FLOAT

Oracle FLOAT。

这与FLOAT相同,不同之处在于接受一个 Oracle 特定的FLOAT.binary_precision参数,并且不接受Float.precision参数。

Oracle FLOAT 类型以“二进制精度”表示精度,默认为 126。对于 REAL 类型,该值为 63。该参数不清晰地映射到特定数量的小数位,但大致相当于所需小数位数除以 0.3103。

新版本 2.0 中新增。

成员

init()

类签名

sqlalchemy.dialects.oracle.FLOAT (sqlalchemy.types.FLOAT)

method __init__(binary_precision=None, asdecimal=False, decimal_return_scale=None)

构造一个浮点数

参数:

  • binary_precision – 要在 DDL 中呈现的 Oracle 二进制精度值。这可以使用公式“十进制精度 = 0.30103 * 二进制精度”来近似为十进制字符的数量。Oracle 用于 FLOAT / DOUBLE PRECISION 的默认值为 126。

  • asdecimal – 参见Float.asdecimal

  • decimal_return_scale – 参见Float.decimal_return_scale

class sqlalchemy.dialects.oracle.INTERVAL

成员

init()

类签名

sqlalchemy.dialects.oracle.INTERVAL (sqlalchemy.types.NativeForEmulated, sqlalchemy.types._AbstractInterval)

method __init__(day_precision=None, second_precision=None)

构造一个间隔。

请注意,目前仅支持“DAY TO SECOND”间隔。这是由于可用的 DBAPI 不支持“YEAR TO MONTH”间隔。

参数:

  • day_precision – 天精度值。这是用于存储天字段的数字位数。默认为“2”

  • second_precision – 秒精度值。这是用于存储小数秒字段的数字位数。默认为“6”。

class sqlalchemy.dialects.oracle.NCLOB

成员

init()

类签名

class sqlalchemy.dialects.oracle.NCLOB (sqlalchemy.types.Text)

method __init__(length: int | None = None, collation: str | None = None)

继承自 Stringsqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 DDL 中使用length,如果包含没有长度的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 
    

    注意

    在大多数情况下,Column 预计存储非 ASCII 数据的列应使用UnicodeUnicodeText数据类型。这些数据类型将确保在数据库上使用正确的类型。

attribute sqlalchemy.dialects.oracle.NVARCHAR2

别名为 NVARCHAR

class sqlalchemy.dialects.oracle.NUMBER

类签名

class sqlalchemy.dialects.oracle.NUMBER (sqlalchemy.types.Numeric, sqlalchemy.types.Integer)

class sqlalchemy.dialects.oracle.LONG

成员

init()

类签名

class sqlalchemy.dialects.oracle.LONG (sqlalchemy.types.Text)

method __init__(length: int | None = None, collation: str | None = None)

继承自 Stringsqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 DDL 中���用length,如果包含没有长度的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 
    

    注意

    在大多数情况下,应该使用 UnicodeUnicodeText 数据类型来存储非 ASCII 数据的 Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.oracle.RAW

类签名

sqlalchemy.dialects.oracle.RAWsqlalchemy.types._Binary

class sqlalchemy.dialects.oracle.ROWID

Oracle ROWID 类型。

当在 cast() 或类似情况下使用时,生成 ROWID。

类签名

sqlalchemy.dialects.oracle.ROWIDsqlalchemy.types.TypeEngine

class sqlalchemy.dialects.oracle.TIMESTAMP

Oracle 实现的 TIMESTAMP,支持附加的 Oracle 特定模式

2.0 版本中的新增功能。

成员

init()

类签名

sqlalchemy.dialects.oracle.TIMESTAMPsqlalchemy.types.TIMESTAMP

method __init__(timezone: bool = False, local_timezone: bool = False)

构造一个新的 TIMESTAMP

参数:

  • timezone – 布尔值。指示 TIMESTAMP 类型应该使用 Oracle 的 TIMESTAMP WITH TIME ZONE 数据类型。

  • local_timezone – 布尔值。指示 TIMESTAMP 类型应该使用 Oracle 的 TIMESTAMP WITH LOCAL TIME ZONE 数据类型。

cx_Oracle

通过 cx-Oracle 驱动程序支持 Oracle 数据库。

DBAPI

cx-Oracle 的文档和下载信息(如果适用)可在此处找到:oracle.github.io/python-cx_Oracle/

连接

连接字符串:

oracle+cx_oracle://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

DSN vs. 主机名连接

cx_Oracle 提供了几种指示目标数据库的方法。方言从一系列不同的 URL 形式转换而来。

使用 Easy Connect 语法的主机名连接

给定目标 Oracle 数据库的主机名、端口和服务名称,例如来自 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")

完整的 Easy Connect 语法不受支持。请使用 tnsnames.ora 文件,并使用 DSN 进行连接。

与 tnsnames.ora 或 Oracle Cloud 的连接

或者,如果未提供端口、数据库名称或 service_name,则方言将使用 Oracle DSN “连接字符串”。这将“主机名”部分的 URL 作为数据源名称。例如,如果 tnsnames.ora 文件包含以下内容的 Net Service Name

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/?encoding=UTF-8&nencoding=UTF-8")

Oracle Cloud 的用户应该使用此语法,并按照 cx_Oracle 文档中所示配置云钱包 连接到自主数据库

SID 连接

要使用 Oracle 的过时 SID 连接语法,SID 可以在 URL 的“数据库名称”部分中传递,如下所示:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:1521/dbname?encoding=UTF-8&nencoding=UTF-8")

上面,传递给 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)))'

传递 cx_Oracle 连接参数

通常可以通过 URL 查询字符串传递其他连接参数;特定符号如 cx_Oracle.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 中,encodingnencoding 的默认值已更改为“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 方言接受的参数如下:

  • arraysize - 在游标上设置 cx_oracle.arraysize 值;默认为 None,表示应使用驱动程序的默认值(通常该值为 100)。此设置控制在获取行时缓冲多少行,并且在修改时可能对性能产生重大影响。该设置用于 cx_Oracle 以及 oracledb

    从版本 2.0.26 起更改:- 将默认值从 50 更改为 None,以使用驱动程序本身的默认值。

  • auto_convert_lobs - 默认为 True;详见 LOB 数据类型。

  • coerce_to_decimal - 详见精确数值。

  • encoding_errors - 详见编码错误。

使用 cx_Oracle SessionPool

cx_Oracle 库提供了自己的连接池实现,可以用来替代 SQLAlchemy 的连接池功能。这可以通过使用create_engine.creator参数提供一个返回新连接的函数,以及设置create_engine.pool_classNullPool来禁用 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=2, max=5, 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 和应用连续性

使用 Oracle 数据库 Resident Connection Pooling(DRCP)

在使用 Oracle 的DRCP时,最佳实践是在从 SessionPool 获取连接时传递连接类和“纯度”。请参阅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 像大多数其他 Oracle 驱动程序一样将使用此环境变量作为其编码配置的来源。此变量的格式是特殊的;典型值可能是AMERICAN_AMERICA.AL32UTF8

cx_Oracle 驱动程序还支持一种编程替代方案,即直接将encodingnencoding参数传递给其.connect()函数。这些可以在 URL 中如下所示:

engine = create_engine("oracle+cx_oracle://scott:tiger@orclpdb/?encoding=UTF-8&nencoding=UTF-8")

有关encodingnencoding参数的含义,请参阅字符集和国家语言支持(NLS)

另请参阅

在 cx_Oracle 文档中 字符集和国家语言支持 (NLS)

Unicode 特定的列数据类型

核心表达语言通过使用 UnicodeUnicodeText 数据类型处理 Unicode 数据。这些类型默认对应于 VARCHAR2 和 CLOB Oracle 数据类型。当使用这些数据类型处理 Unicode 数据时,期望 Oracle 数据库配置有 Unicode-aware 字符集,并且 NLS_LANG 环境变量已适当设置,以便 VARCHAR2 和 CLOB 数据类型能够容纳数据。

如果 Oracle 数据库未配置 Unicode 字符集,则有两种选择:显式使用 NCHARNCLOB 数据类型,或者在调用 create_engine() 时传递标志 use_nchar_for_unicode=True,这将导致 SQLAlchemy 方言使用 NCHAR/NCLOB 替代 VARCHAR/CLOB 用于 Unicode / UnicodeText 数据类型。

1.3 版本更改:UnicodeUnicodeText 数据类型现在默认对应于 VARCHAR2CLOB Oracle 数据类型,除非在调用 create_engine() 时传递了 use_nchar_for_unicode=True

编码错误

对于 Oracle 数据库中存在损坏编码的情况,方言接受一个参数 encoding_errors,该参数将传递给 Unicode 解码函数,以影响如何处理解码错误。该值最终由 Python decode 函数消耗,并且通过 cx_Oracle 的 encodingErrors 参数和 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 内置数据类型符号列表,网址为cx-oracle.readthedocs.io/en/latest/api_manual/module.html#database-types。请注意,在某些情况下,使用这些类型可能会导致显著的性能下降,尤其是在指定cx_Oracle.CLOB时。

在 SQLAlchemy 方面,可以使用DialectEvents.do_setinputsizes()事件来实现运行时可见性(例如日志记录)和完全控制每个语句上如何使用setinputsizes()

版本 1.2.9 中新增:添加了DialectEvents.setinputsizes()

示例 1 - 记录所有 setinputsizes 调用

下面的示例说明了如何在将其转换为原始setinputsizes()参数字典之前,从 SQLAlchemy 视角记录中间值。字典的键是具有.key.type属性的BindParameter对象:

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]
```### RETURNING 支持

cx_Oracle 方言使用 OUT 参数实现 RETURNING。该方言完全支持 RETURNING。### LOB 数据类型

LOB 数据类型指的是诸如 CLOB、NCLOB 和 BLOB 等“大对象”数据类型。cx_Oracle 和 oracledb 的现代版本经过优化,使得这些数据类型能够作为单个缓冲区传递。因此,默认情况下,SQLAlchemy 使用这些较新的类型处理程序。

要禁用较新的类型处理程序,并将 LOB 对象作为具有`read()`方法的经典缓冲对象传递,请将参数`auto_convert_lobs=False`传递给`create_engine()`,该参数仅对整个引擎生效。

### 不支持两阶段事务

由于驱动程序支持不佳,cx_Oracle 不支持两阶段事务。 从 cx_Oracle 6.0b1 开始,两阶段事务的接口已更改为更直接地通过底层 OCI 层进行传递,并减少了自动化。 支持此系统的附加逻辑未在 SQLAlchemy 中实现。

### 精确数字

SQLAlchemy 的数字类型可以将值作为 Python `Decimal` 对象或 float 对象接收和返回。 当使用 `Numeric` 对象或其子类(如 `Float`,`DOUBLE_PRECISION` 等)时, `Numeric.asdecimal` 标志决定是否应在返回时将值强制转换为 `Decimal`,或以 float 对象返回。 在 Oracle 下情况更加复杂,如果“scale”为零,Oracle 的 `NUMBER` 类型还可以表示整数值,因此 Oracle 特定的 `NUMBER` 类型也考虑了这一点。

cx_Oracle 方言广泛使用连接和游标级别的“outputtypehandler”可调用来根据需要强制转换数值。 这些可调用是针对正在使用的具体 `Numeric` 的特定风味的,以及如果不存在 SQLAlchemy 类型化对象。 已观察到的情况包括 Oracle 可能发送有关返回的数字类型的不完整或模糊信息的情况,例如查询,其中数字类型被嵌套在多个子查询的多个级别下。 类型处理程序在所有情况下都尽力做出正确的决定,在所有情况下都将决策委托给底层的 cx_Oracle DBAPI,以便在驱动程序可以做出最佳决策的所有情况下进行。

当不存在类型化对象时,例如在执行纯 SQL 字符串时,存在默认的“outputtypehandler”,该处理程序通常将指定精度和比例的数字值作为 Python `Decimal` 对象返回。 为了性能原因禁用此转换为十进制数的操作,请将标志 `coerce_to_decimal=False` 传递给 `create_engine()`:

```py
engine = create_engine("oracle+cx_oracle://dsn", coerce_to_decimal=False)

coerce_to_decimal 标志仅影响与 Numeric SQLAlchemy 类型(或其子类)无关联的纯字符串 SQL 语句的结果。

自 1.2 版本起进行了更改:cx_Oracle 的数字处理系统已经重写,以利用较新的 cx_Oracle 特性以及更好地集成输出类型处理程序。

DBAPI

cx-Oracle 的文档和下载信息(如适用)可在此处获取。

连接

连接字符串:

oracle+cx_oracle://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

DSN vs. 主机名连接

cx_Oracle 提供了几种指示目标数据库的方法。方言将一系列不同的 URL 形式进行转换。

使用简易连接语法连接主机名

给定目标 Oracle 数据库的主机名、端口和服务名,例如来自 Oracle 的简易连接语法,然后在 SQLAlchemy 中使用service_name查询字符串参数进行连接:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:port/?service_name=myservice&encoding=UTF-8&nencoding=UTF-8")

不支持完整的简易连接语法。而是使用tnsnames.ora文件,并使用 DSN 进行连接。

使用 tnsnames.ora 或 Oracle Cloud 进行连接

或者,如果未提供端口、数据库名称或service_name,则方言将使用 Oracle DSN “连接字符串”。这将 URL 的“主机名”部分作为数据源名称。例如,如果tnsnames.ora文件包含如下的网络服务名称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/?encoding=UTF-8&nencoding=UTF-8")

Oracle Cloud 的用户应使用此语法,并按照 cx_Oracle 文档连接到 Autonomous 数据库中所示配置云钱包。

SID 连接

要使用 Oracle 的过时 SID 连接语法,可以如下传递 SID 在 URL 的“数据库名称”部分:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:1521/dbname?encoding=UTF-8&nencoding=UTF-8")

在上述代码中,传递给 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)))'

使用简易连接语法连接主机名

给定目标 Oracle 数据库的主机名、端口和服务名,例如来自 Oracle 的简易连接语法,然后在 SQLAlchemy 中使用service_name查询字符串参数进行连接:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:port/?service_name=myservice&encoding=UTF-8&nencoding=UTF-8")

不支持完整的简易连接语法。而是使用tnsnames.ora文件,并使用 DSN 进行连接。

使用 tnsnames.ora 或 Oracle Cloud 进行连接

或者,如果没有提供端口、数据库名称或service_name,则方言将使用 Oracle DSN “连接字符串”。这将 URL 的“主机名”部分作为数据源名称。例如,如果tnsnames.ora文件包含如下的网络服务名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/?encoding=UTF-8&nencoding=UTF-8")

Oracle Cloud 的用户应使用此语法,并按照 cx_Oracle 文档中显示的方式配置云钱包连接到自主数据库

SID 连接

要使用 Oracle 的过时 SID 连接语法,SID 可以在 URL 的“数据库名称”部分中传递,如下所示:

engine = create_engine("oracle+cx_oracle://scott:tiger@hostname:1521/dbname?encoding=UTF-8&nencoding=UTF-8")

上面,传递给 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)))'

传递 cx_Oracle 连接参数

通常可以通过 URL 查询字符串传递其他连接参数;像cx_Oracle.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 中,encodingnencoding的默认值已更改为“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 方言接受的参数如下:

  • arraysize - 设置光标上的 cx_oracle.arraysize 值;默认为None,表示应使用驱动程序的默认值(通常值为 100)。此设置控制在提取行时缓冲多少行,并且在修改时可能会对性能产生显着影响。该设置用于cx_Oracle以及oracledb

    改变版本 2.0.26:- 将默认值从 50 更改为 None,以使用驱动程序本身的默认值。

  • auto_convert_lobs - 默认为 True;请参阅 LOB 数据类型。

  • coerce_to_decimal - 详情请参阅精确数字。

  • encoding_errors - 详情请参阅编码错误。

使用 cx_Oracle SessionPool

cx_Oracle 库提供了自己的连接池实现,可以代替 SQLAlchemy 的池功能。这可以通过使用 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=2, max=5, 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 和应用程序连续性

使用 Oracle 数据库常驻连接池(DRCP)

当使用 Oracle 的DRCP时,最佳实践是在从 SessionPool 获取连接时传递连接类和“纯度”。参考 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环境变量。像大多数其他 Oracle 驱动程序一样,cx_Oracle 将使用此环境变量作为其编码配置的源。该变量的格式是特殊的;典型值可能是 AMERICAN_AMERICA.AL32UTF8

cx_Oracle 驱动程序还支持一种编程替代方法,即直接将 encodingnencoding 参数传递给其 .connect() 函数。这些可以在 URL 中存在如下:

engine = create_engine("oracle+cx_oracle://scott:tiger@orclpdb/?encoding=UTF-8&nencoding=UTF-8")

关于 encodingnencoding 参数的含义,请参阅字符集和国家语言支持(NLS)

另请参阅

字符集和国家语言支持 (NLS) - 在 cx_Oracle 文档中。

Unicode 特定的列数据类型

核心表达式语言通过使用 UnicodeUnicodeText 数据类型处理 Unicode 数据。这些类型默认对应于 VARCHAR2 和 CLOB Oracle 数据类型。当使用这些数据类型处理 Unicode 数据时,期望 Oracle 数据库配置了 Unicode-aware 字符集,并且 NLS_LANG 环境变量被适当设置,以便 VARCHAR2 和 CLOB 数据类型可以容纳数据。

如果 Oracle 数据库未配置为 Unicode 字符集,则两个选项是显式使用 NCHARNCLOB 数据类型,或者在调用 create_engine() 时传递 use_nchar_for_unicode=True 标志,这将导致 SQLAlchemy 方言对 Unicode / UnicodeText 数据类型使用 NCHAR/NCLOB 而不是 VARCHAR/CLOB。

版本 1.3 中的变更:UnicodeUnicodeText 数据类型现在对应于 VARCHAR2CLOB 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 中新增。

确保正确的客户端编码

几乎所有与 Oracle 相关的软件建立客户端编码的长期接受标准是通过 NLS_LANG 环境变量。cx_Oracle 像大多数其他 Oracle 驱动程序一样将使用此环境变量作为其编码配置的来源。该变量的格式是特殊的;典型值可能是 AMERICAN_AMERICA.AL32UTF8

cx_Oracle 驱动程序还支持一种编程方式,即直接将 encodingnencoding 参数传递给其 .connect() 函数。可以在 URL 中以以下方式存在:

engine = create_engine("oracle+cx_oracle://scott:tiger@orclpdb/?encoding=UTF-8&nencoding=UTF-8")

关于 encodingnencoding 参数的含义,请参阅字符集和国家语言支持(NLS)

参见

字符集和国家语言支持(NLS) - 在 cx_Oracle 文档中。

Unicode 特定列数据类型

核心表达式语言通过使用 UnicodeUnicodeText 数据类型处理 Unicode 数据。这些类型默认对应于 VARCHAR2 和 CLOB Oracle 数据类型。当使用这些数据类型处理 Unicode 数据时,预期 Oracle 数据库已配置为使用 Unicode 意识字符集,并且 NLS_LANG 环境变量已适当设置,以便 VARCHAR2 和 CLOB 数据类型可以容纳数据。

如果 Oracle 数据库未配置为 Unicode 字符集,则两个选项是显式使用 NCHARNCLOB 数据类型,或者在调用 create_engine() 时传递标志 use_nchar_for_unicode=True 给 SQLAlchemy 方言,这将导致 SQLAlchemy 方言在 Unicode / UnicodeText 数据类型上使用 NCHAR/NCLOB 而不是 VARCHAR/CLOB。

从版本 1.3 开始更改:UnicodeUnicodeText 数据类型现在对应于 VARCHAR2CLOB 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 内置数据类型符号的列表,网址为 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 调用

以下示例说明了如何在转换为原始 setinputsizes() 参数字典之前从 SQLAlchemy 视角记录中间值。字典的键是具有 .key.type 属性的 BindParameter 对象:

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]

示例 1 - 记录所有 setinputsizes 调用

以下示例说明了如何在 SQLAlchemy 视角下记录中间值,然后再将它们转换为原始setinputsizes()参数字典。字典的键是具有.key.type属性的BindParameter对象:

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]

RETURNING 支持

cx_Oracle 方言使用 OUT 参数实现 RETURNING。该方言完全支持 RETURNING。

LOB 数据类型

LOB 数据类型指的是诸如 CLOB、NCLOB 和 BLOB 等“大对象”数据类型。现代版本的 cx_Oracle 和 oracledb 都经过优化,以便将这些数据类型作为单个缓冲区传递。因此,默认情况下 SQLAlchemy 使用这些较新的类型处理程序。

要禁用较新类型处理程序的使用,并将 LOB 对象作为具有read()方法的经典缓冲对象传递,可以将参数auto_convert_lobs=False传递给create_engine(),这仅在整个引擎范围内生效。

不支持两阶段事务

由于 cx_Oracle 的驱动程序支持不佳,cx_Oracle 不支持两阶段事务。从 cx_Oracle 6.0b1 开始,用于两阶段事务的接口已更改为更直接地通过到底层 OCI 层的传递,自动化程度较低。支持此系统的附加逻辑未在 SQLAlchemy 中实现。

精确数值

SQLAlchemy 的数值类型可以处理接收和返回 Python Decimal 对象或浮点对象的值。当使用 Numeric 对象或其子类如 FloatDOUBLE_PRECISION 等时,Numeric.asdecimal 标志确定返回时值是否应强制转换为 Decimal,或作为浮点对象返回。在 Oracle 下更加复杂的是,如果“scale”为零,Oracle 的 NUMBER 类型也可以表示整数值,因此 Oracle 特定的 NUMBER 类型也考虑到了这一点。

cx_Oracle 方言广泛使用连接和游标级别的“outputtypehandler”可调用对象,以按请求强制转换数值。这些可调用对象特定于正在使用的特定Numeric的类型,以及如果没有 SQLAlchemy 类型化对象存在。已经观察到 Oracle 可能会发送关于返回的数值类型不完整或模糊的信息的情况,例如查询,其中数值类型被埋在多级子查询下。类型处理程序尽最大努力在所有情况下做出正确的决定,在所有情况下都推迟到底层 cx_Oracle DBAPI,以便在驱动程序可以做出最佳决定的所有这些情况下。

当没有类型化对象时,例如执行纯 SQL 字符串时,存在一个默认的“outputtypehandler”,通常返回指定精度和比例的数值,其类型为 Python 的Decimal对象。为了出于性能考虑禁用对十进制数的强制转换,请在create_engine()中传递标志coerce_to_decimal=False

engine = create_engine("oracle+cx_oracle://dsn", coerce_to_decimal=False)

coerce_to_decimal标志仅影响不与Numeric SQLAlchemy 类型(或其子类)相关联的纯字符串 SQL 语句的结果。

从 1.2 版本开始更改:cx_Oracle 的数值处理系统已经重新设计,以利用较新的 cx_Oracle 功能以及更好地集成 outputtypehandlers。

python-oracledb

通过 python-oracledb 驱动程序支持 Oracle 数据库。

DBAPI

有关 python-oracledb 的文档和下载信息(如果适用),请访问:oracle.github.io/python-oracledb/

连接

连接字符串:

oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

python-oracledb 是由 Oracle 发布的,旨在取代 cx_Oracle 驱动程序。它与 cx_Oracle 完全兼容,具有不需要任何依赖项的“轻客户端”模式,以及使用与 cx_Oracle 相同的方式使用 Oracle Client Interface 的“厚客户端”模式。

另请参阅

cx_Oracle - cx_Oracle 的所有注意事项也适用于 oracledb 驱动程序。

SQLAlchemy 的oracledb方言提供了同名的同步和异步实现。根据引擎的创建方式选择合适的版本:

  • 使用oracle+oracledb://...调用create_engine()将自动选择同步版本,例如:

    from sqlalchemy import create_engine
    sync_engine = create_engine("oracle+oracledb://scott:tiger@localhost/?service_name=XEPDB1")
    
  • 使用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=XEPDB1")
    

可以明确指定方言的异步版本,例如使用oracledb_async后缀:

from sqlalchemy.ext.asyncio import create_async_engine
asyncio_engine = create_async_engine("oracle+oracledb_async://scott:tiger@localhost/?service_name=XEPDB1")

新版本 2.0.25 中新增对 oracledb 的异步版本的支持。

Thick mode 支持

默认情况下,python-oracledb 以 thin 模式启动,不需要在系统中安装 Oracle 客户端库。python-oracledb 驱动程序还支持一种“thick”模式,其行为类似于cx_oracle,并且要求安装 Oracle 客户端接口(OCI)。

要启用此模式,用户可以手动调用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", "driver_name": "my-app"
})

另请参见

python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.init_oracle_client

新版本 2.0.0 中新增对 oracledb 驱动程序的支持。

DBAPI

python-oracledb 的文档和下载信息(如适用)可在此处找到:oracle.github.io/python-oracledb/

连接

连接字符串:

oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]]

Thick mode 支持

默认情况下,python-oracledb 以 thin 模式启动,不需要在系统中安装 Oracle 客户端库。python-oracledb 驱动程序还支持一种“thick”模式,其行为类似于cx_oracle,并且要求安装 Oracle 客户端接口(OCI)。

要启用此模式,用户可以手动调用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", "driver_name": "my-app"
})

另请参见

python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.init_oracle_client

新版本 2.0.0 中新增对 oracledb 驱动程序的支持。

Microsoft SQL Server

原文:docs.sqlalchemy.org/en/20/dialects/mssql.html

对 Microsoft SQL Server 数据库的支持。

下表总结了当前数据库发布版本的支持水平。

支持的 Microsoft SQL Server 版本

支持类型 版本
CI 全面测试 2017
正常支持 2012+
尽力而为 2005+

DBAPI 支持

可用的方言/DBAPI 选项如下。请参考各个 DBAPI 部分获取连接信息。

  • PyODBC

  • pymssql

  • aioodbc

外部方言

除了上述具有原生 SQLAlchemy 支持的 DBAPI 层之外,还有适用于 SQL Server 的与第三方方言兼容的其他 DBAPI 层。请参阅 Dialects 页面上的“外部方言”列表。 ## 自动递增行为 / IDENTITY 列

SQL Server 使用 IDENTITY 结构提供所谓的“自动递增”行为,可以放置在表中的任何单个整数列上。SQLAlchemy 将 IDENTITY 视为整数主键列的默认“autoincrement”行为的一部分,该行为在 Column.autoincrement 中描述。这意味着默认情况下,Table 中的第一个整数主键列将被视为标识列 - 除非它与 Sequence 关联 - 并将生成 DDL 如下:

from sqlalchemy import Table, MetaData, Column, Integer

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True),
        Column('x', Integer))
m.create_all(engine)

上述示例将生成 DDL 如下:

CREATE  TABLE  t  (
  id  INTEGER  NOT  NULL  IDENTITY,
  x  INTEGER  NULL,
  PRIMARY  KEY  (id)
)

对于不希望使用默认生成的 IDENTITY 的情况,请在第一个整数主键列上将 Column.autoincrement 标志设置为 False

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True, autoincrement=False),
        Column('x', Integer))
m.create_all(engine)

要将 IDENTITY 关键字添加到非主键列,请在所需的 Column 对象上将 Column.autoincrement 标志设置为 True,并确保在任何整数主键列上将 Column.autoincrement 设置为 False

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True, autoincrement=False),
        Column('x', Integer, autoincrement=True))
m.create_all(engine)

自 1.4 版更改:在 Column 中添加了 Identity 结构,以指定 IDENTITY 的起始和增量参数。这些取代了使用 Sequence 对象来指定这些值。

自 1.4 版弃用:Columnmssql_identity_startmssql_identity_increment 参数已被弃用,应该用 Identity 对象替换。同时指定两种配置 IDENTITY 的方式将导致编译错误。这些选项也不再作为 Inspector.get_columns()dialect_options 键的一部分返回。请使用 identity 键中的信息。

自 1.3 版弃用:使用 Sequence 指定 IDENTITY 特性已被弃用,并将在未来版本中删除。请使用 Identity 对象参数 Identity.startIdentity.increment

自 1.4 版更改:移除了使用 Sequence 对象修改 IDENTITY 特性的能力。Sequence 对象现在仅操作真正的 T-SQL SEQUENCE 类型。

注意

表中只能有一个 IDENTITY 列。当使用 autoincrement=True 启用 IDENTITY 关键字时,SQLAlchemy 不会防止多个列同时指定该选项。SQL Server 数据库将拒绝 CREATE TABLE 语句。

注意

尝试为标记为 IDENTITY 的列提供值的 INSERT 语句将被 SQL Server 拒绝。为了接受该值,必须启用会话级选项“SET IDENTITY_INSERT”。当使用核心 Insert 构造时,SQLAlchemy SQL Server 方言将在执行指定 IDENTITY 列的值时自动执行此操作;如果执行为该语句的调用启用了“IDENTITY_INSERT”选项。然而,这种情况性能不高,不应依赖于正常使用。如果表实际上不需要其整数主键列的 IDENTITY 行为,则在创建表时应禁用该关键字,确保设置 autoincrement=False

控制“Start”和“Increment”

通过传递给 Identity 对象的 Identity.startIdentity.increment 参数,可以对 IDENTITY 生成器的“start”和“increment”值进行具体控制:

from sqlalchemy import Table, Integer, Column, Identity

test = Table(
    'test', metadata,
    Column(
        'id',
        Integer,
        primary_key=True,
        Identity(start=100, increment=10)
    ),
    Column('name', String(20))
)

上述 Table 对象的 CREATE TABLE 将是:

CREATE  TABLE  test  (
  id  INTEGER  NOT  NULL  IDENTITY(100,10)  PRIMARY  KEY,
  name  VARCHAR(20)  NULL,
  )

注意

Identity 对象支持许多其他参数,除了 startincrement 之外。这些参数不受 SQL Server 支持,在生成 CREATE TABLE ddl 时将被忽略。

从版本 1.3.19 更改:Identity 对象现在用于影响 SQL Server 下的 ColumnIDENTITY 生成器。以前使用 Sequence 对象。由于 SQL Server 现在支持真正的序列作为一个单独的构造,Sequence 将从 SQLAlchemy 版本 1.4 开始以正常方式运行。

使用非整数数值类型的 IDENTITY

SQL Server 也允许将 IDENTITY 用于 NUMERIC 列。为了在 SQLAlchemy 中顺利实现这种模式,列的主要数据类型应保持为 Integer,但是可以使用 TypeEngine.with_variant() 指定在 SQL Server 数据库中部署的底层实现类型为 Numeric

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Numeric
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(Numeric(10, 0), "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

在上述示例中,Integer().with_variant()提供了清晰的使用信息,准确描述了代码的意图。autoincrement仅适用于Integer的一般限制是在元数据级别而不是每个方言级别建立的。

使用上述模式时,从行插入返回的主键标识符(也是将分配给诸如上面的TestTable之类的 ORM 对象的值)在使用 SQL Server 时将是Decimal()的实例,而不是int。通过向Numeric类型的Numeric.asdecimal传递 False 来更改上述Numeric(10, 0)的返回类型以返回浮点数。为了将上述Numeric(10, 0)的返回类型规范化为返回 Python int(Python 3 中还支持“长”整数值),请使用TypeDecorator如下所示:

from sqlalchemy import TypeDecorator

class NumericAsInteger(TypeDecorator):
  '''normalize floating point return values into ints'''

    impl = Numeric(10, 0, asdecimal=False)
    cache_ok = True

    def process_result_value(self, value, dialect):
        if value is not None:
            value = int(value)
        return value

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(NumericAsInteger, "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

INSERT 行为

在 INSERT 时处理 IDENTITY 列涉及两个关键技术。最常见的是能够获取给定 IDENTITY 列的“最后插入值”,这是 SQLAlchemy 在许多情况下隐式执行的过程,最重要的是在 ORM 中。

获取此值的过程有几种变体:

  • 在绝大多数情况下,RETURNING 与 SQL Server 上的 INSERT 语句一起使用,以获取新生成的主键值:

    INSERT  INTO  t  (x)  OUTPUT  inserted.id  VALUES  (?)
    

    从 SQLAlchemy 2.0 开始,默认还使用 “插入多个值”行为适用于 INSERT 语句 功能来优化多行 INSERT 语句;对于 SQL Server,该功能适用于 RETURNING 和非 RETURNING INSERT 语句。

    在版本 2.0.10 中更改:由于与行排序问题有关,SQLAlchemy 版本 2.0.9 的 SQL Server 的 “插入多个值”行为适用于 INSERT 语句 功能暂时被禁用。从 2.0.10 开始,该功能已重新启用,并针对工作单元对 RETURNING 的要求进行了特殊处理,以进行排序。

  • 当 RETURNING 不可用或已通过implicit_returning=False禁用时,将使用scope_identity()函数或@@identity变量;后端的行为各不相同:

    • 使用 PyODBC 时,短语; select scope_identity()将附加到 INSERT 语句的末尾;将获取第二个结果集以接收该值。给定表格为:

      t = Table(
          't',
          metadata,
          Column('id', Integer, primary_key=True),
          Column('x', Integer),
          implicit_returning=False
      )
      

      INSERT 将如下所示:

      INSERT  INTO  t  (x)  VALUES  (?);  select  scope_identity()
      
    • 其他方言如 pymssql 在 INSERT 语句之后调用 SELECT scope_identity() AS lastrowid。如果将标志 use_scope_identity=False 传递给 create_engine(),则会使用语句 SELECT @@identity AS lastrowid

包含 IDENTITY 列的表将禁止明确引用标识列的 INSERT 语句。当使用核心 insert() 构造(而不是纯字符串 SQL)创建的 INSERT 构造引用标识列时,SQLAlchemy 方言将检测到,并且在此情况下将在执行 INSERT 语句之前发出 SET IDENTITY_INSERT ON,并在执行后发出 SET IDENTITY_INSERT OFF。例如:

m = MetaData()
t = Table('t', m, Column('id', Integer, primary_key=True),
                Column('x', Integer))
m.create_all(engine)

with engine.begin() as conn:
    conn.execute(t.insert(), {'id': 1, 'x':1}, {'id':2, 'x':2})

上述列将使用 IDENTITY 创建,但我们发出的 INSERT 语句指定了显式值。在回声输出中,我们可以看到 SQLAlchemy 如何处理这种情况:

CREATE  TABLE  t  (
  id  INTEGER  NOT  NULL  IDENTITY(1,1),
  x  INTEGER  NULL,
  PRIMARY  KEY  (id)
)

COMMIT
SET  IDENTITY_INSERT  t  ON
INSERT  INTO  t  (id,  x)  VALUES  (?,  ?)
((1,  1),  (2,  2))
SET  IDENTITY_INSERT  t  OFF
COMMIT

这是适用于测试和大量插入场景的辅助用例。

SEQUENCE 支持

Sequence 对象创建“真正”的序列,即 CREATE SEQUENCE

>>> from sqlalchemy import Sequence
>>> from sqlalchemy.schema import CreateSequence
>>> from sqlalchemy.dialects import mssql
>>> print(CreateSequence(Sequence("my_seq", start=1)).compile(dialect=mssql.dialect()))
CREATE  SEQUENCE  my_seq  START  WITH  1 

对于整数主键生成,通常应优先选择 SQL Server 的 IDENTITY 构造而不是序列。

提示

T-SQL 的默认起始值为 -2**63,而不是大多数其他 SQL 数据库中的 1。如果这是预期的默认值,则用户应明确设置 Sequence.start 为 1:

seq = Sequence("my_sequence", start=1)

新版 1.4 中新增 SQL Server 对 Sequence 的支持

从版本 2.0 起更改:SQL Server 方言不再为 CREATE SEQUENCE 隐式呈现“START WITH 1”,这是在版本 1.4 中首次实现的行为。

VARCHAR / NVARCHAR 上的 MAX

SQL Server 支持特殊字符串“MAX”在 VARCHARNVARCHAR 数据类型中,表示“最大可能长度”。当前的方言将此处理为基本类型中的长度“None”,而不是提供这些类型的方言特定版本,因此指定基本类型如 VARCHAR(None) 可以在不同的后端上假定“无长度”的行为而不使用方言特定的类型。

要构建具有 MAX 长度的 SQL Server VARCHAR 或 NVARCHAR,请使用 None:

my_table = Table(
    'my_table', metadata,
    Column('my_data', VARCHAR(None)),
    Column('my_n_data', NVARCHAR(None))
)

校对支持

字符排序由字符串参数“collation”指定的基本字符串类型支持:

from sqlalchemy import VARCHAR
Column('login', VARCHAR(32, collation='Latin1_General_CI_AS'))

当这样的列与 Table 关联时,此列的 CREATE TABLE 语句将产生:

login VARCHAR(32) COLLATE Latin1_General_CI_AS NULL

LIMIT/OFFSET 支持

MSSQL 自 SQL Server 2012 起已添加了对 LIMIT / OFFSET 的支持,通过“OFFSET n ROWS”和“FETCH NEXT n ROWS”子句。如果检测到 SQL Server 2012 或更高版本,SQLAlchemy 将自动支持这些语法。

1.4 版本更改:增加了对 SQL Server “OFFSET n ROWS” 和 “FETCH NEXT n ROWS” 语法的支持。

对于仅指定 LIMIT 而不带 OFFSET 的语句,所有版本的 SQL Server 都支持 TOP 关键字。当没有 OFFSET 子句时,此语法用于所有 SQL Server 版本。例如:

select(some_table).limit(5)

将类似于渲染:

SELECT TOP 5 col1, col2.. FROM table

对于早于 SQL Server 2012 的 SQL Server 版本,使用 LIMIT 和 OFFSET 或仅 OFFSET 的语句将使用 ROW_NUMBER() 窗口函数进行渲染。例如:

select(some_table).order_by(some_table.c.col3).limit(5).offset(10)

将类似于渲染:

SELECT anon_1.col1, anon_1.col2 FROM (SELECT col1, col2,
ROW_NUMBER() OVER (ORDER BY col3) AS
mssql_rn FROM table WHERE t.x = :x_1) AS
anon_1 WHERE mssql_rn > :param_1 AND mssql_rn <= :param_2 + :param_1

注意,当使用 LIMIT 和/或 OFFSET 时,无论是使用较旧还是较新的 SQL Server 语法,语句都必须有 ORDER BY,否则会引发 CompileError

DDL 注释支持

注释支持包括对 Table.commentColumn.comment 等属性的 DDL 渲染,以及反映这些注释的能力,假设使用的 SQL Server 版本支持。如果在首次连接时检测到不支持的版本,如 Azure Synapse(基于 fn_listextendedproperty SQL 函数的存在),则禁用注释支持,包括渲染和表注释反射,因为这两个功能都依赖于并非所有后端类型都可用的 SQL Server 存储过程和函数。

要强制启用或禁用注释支持,绕过自动检测,在 create_engine() 中设置参数 supports_comments

e = create_engine("mssql+pyodbc://u:p@dsn", supports_comments=False)

版本 2.0 新增了对 SQL Server 方言的表和列注释的支持,包括 DDL 生成和反射。 ## 事务隔离级别

所有 SQL Server 方言都支持通过方言特定参数create_engine.isolation_level(由create_engine()接受)以及作为传递给Connection.execution_options()的参数的Connection.execution_options.isolation_level来设置事务隔离级别。此功能通过为每个新连接发出SET TRANSACTION ISOLATION LEVEL <level>命令来工作。

使用 create_engine() 设置隔离级别:

engine = create_engine(
    "mssql+pyodbc://scott:tiger@ms_2008",
    isolation_level="REPEATABLE READ"
)

使用每个连接的执行选项来设置:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="READ COMMITTED"
)

isolation_level 的有效值包括:

  • AUTOCOMMIT - 仅适用于 pyodbc / pymssql

  • READ COMMITTED

  • READ UNCOMMITTED

  • REPEATABLE READ

  • SERIALIZABLE

  • SNAPSHOT - 适用于 SQL Server 特定的隔离级别

隔离级别配置还有更多选项,比如与主Engine相关联的“子引擎”对象,每个对象都应用不同的隔离级别设置。有关详情,请参阅设置事务隔离级别,包括 DBAPI 自动提交中的讨论。

另请参阅

设置事务隔离级别,包括 DBAPI 自动提交 ## 临时表 / 资源重置以用于连接池

SQLAlchemy Engine 对象使用的 QueuePool 连接池实现包括在连接返回池时调用 DBAPI 的.rollback()方法的 重置行为,虽然此回滚会清除上一个事务使用的即时状态,但它不包括更广泛的会话级状态,包括临时表以及其他服务器状态,如预编译的语句句柄和语句缓存。一个名为 sp_reset_connection 的未记录的 SQL Server 过程已知可解决此问题,它将重置在连接上建立的大部分会话状态,包括临时表。

要将 sp_reset_connection 安装为执行返回时的重置手段,可以使用 PoolEvents.reset() 事件挂钩,如下面的示例所示。create_engine.pool_reset_on_return 参数设置为 None,以便自定义方案可以完全替换默认行为。自定义挂钩实现在任何情况下都调用 .rollback(),因为通常重要的是 DBAPI 自身对提交/回滚的跟踪将与事务的状态保持一致:

from sqlalchemy import create_engine
from sqlalchemy import event

mssql_engine = create_engine(
    "mssql+pyodbc://scott:tiger⁵HHH@mssql2017:1433/test?driver=ODBC+Driver+17+for+SQL+Server",

    # disable default reset-on-return scheme
    pool_reset_on_return=None,
)

@event.listens_for(mssql_engine, "reset")
def _reset_mssql(dbapi_connection, connection_record, reset_state):
    if not reset_state.terminate_only:
        dbapi_connection.execute("{call sys.sp_reset_connection}")

    # so that the DBAPI itself knows that the connection has been
    # reset
    dbapi_connection.rollback()

从版本 2.0.0b3 中更改:为 PoolEvents.reset() 事件添加了额外的状态参数,并确保该事件对所有“重置”事件都会被调用,以便作为自定义“重置”处理程序的适当位置。以前使用 PoolEvents.checkin() 处理程序的方案仍然可用。

另请参阅

返回时重置 - 在 连接池 文档中

可空性

MSSQL 支持三种列可空性级别。默认的可空性允许空值,并在 CREATE TABLE 构造中明确指定:

name VARCHAR(20) NULL

如果指定了 nullable=None,则不进行任何规定。换句话说,将使用数据库配置的默认值。这将导致:

name VARCHAR(20)

如果 nullableTrueFalse,则列将分别为 NULLNOT NULL

日期/时间处理

DATE 和 TIME 是受支持的。必要时,绑定参数将转换为 datetime.datetime() 对象,大多数 MSSQL 驱动程序都需要这样做,并且如果需要的话,结果将从字符串中进行处理。 DATE 和 TIME 类型对于 MSSQL 2005 及以前的版本不可用 - 如果检测到低于 2008 的服务器版本,则将为这些类型发出 DATETIME 的 DDL。

大型文本/二进制类型弃用

根据 SQL Server 2012/2014 文档NTEXTTEXTIMAGE 数据类型将在将来的版本中从 SQL Server 中删除。SQLAlchemy 通常将这些类型关联到 UnicodeTextTextClauseLargeBinary 数据类型。

为了适应这种变化,为该方言添加了一个新标志 deprecate_large_types,如果用户没有另外设置,则将基于使用的服务器版本自动设置。此标志的行为如下:

  • 当此标志为True时,当用于渲染 DDL 时,UnicodeTextTextClauseLargeBinary数据类型将分别呈现类型NVARCHAR(max)VARCHAR(max)VARBINARY(max)。这是从添加此标志开始的新行为。

  • 当此标志为False时,当用于渲染 DDL 时,UnicodeTextTextClauseLargeBinary数据类型将分别呈现类型NTEXTTEXTIMAGE。这是这些类型的长期行为。

  • 在建立数据库连接之前,标志始于值None。如果使用方言渲染 DDL 而没有设置标志,则其被解释为False

  • 在首次连接时,方言会检测是否使用了 SQL Server 版本 2012 或更高版本;如果标志仍然为None,则基于是否检测到 2012 或更高版本,将其设置为TrueFalse

  • 当方言创建时,可以将标志设置为TrueFalse,通常通过create_engine()来实现:

    eng = create_engine("mssql+pymssql://user:pass@host/db",
                    deprecate_large_types=True)
    
  • 在所有 SQLAlchemy 版本中,始终可以使用大写类型对象完全控制“旧”或“新”类型的渲染:NVARCHARVARCHARVARBINARYTEXTNTEXTIMAGE将始终保持不变,并且始终输出确切的类型。 ## 多部分模式名称

SQL Server 模式有时需要多部分来表示其“模式”限定符,即将数据库名称和所有者名称作为单独的标记,例如mydatabase.dbo.some_table。可以使用Table.schema参数一次设置这些多部分名称。

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="mydatabase.dbo"
)

在执行诸如表或组件反射之类的操作时,包含点的模式参数将被拆分为单独的“数据库”和“所有者”组件,以便正确查询 SQL Server 信息模式表,因为这两个值是分开存储的。此外,在为 DDL 或 SQL 呈现模式名称时,这两个组件将被分别引用以用于区分大小写的名称和其他特殊字符。给定如下参数:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="MyDataBase.dbo"
)

上述模式将呈现为[MyDataBase].dbo,并且在反射中,将使用“dbo”作为所有者和“MyDataBase”作为数据库名称进行反射。

要控制模式名称如何被拆分为数据库/所有者,请在名称中指定括号(在 SQL Server 中是引用字符)。下面,“所有者”将被视为MyDataBase.dbo,而“数据库”将为 None:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="[MyDataBase.dbo]"
)

要单独指定带有特殊字符或嵌入点的数据库和所有者名称,请使用两组括号:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="[MyDataBase.Period].[MyOwner.Dot]"
)

自版本 1.2 更改:SQL Server 方言现在将括号视为标识符分隔符,将模式拆分为单独的数据库和所有者标记,以允许名称本身中的点。 ## 传统模式模式

非常旧版本的 MSSQL 方言引入了这样的行为,即在 SELECT 语句中使用模式限定的表时,将自动为其设置别名;给定一个表:

account_table = Table(
    'account', metadata,
    Column('id', Integer, primary_key=True),
    Column('info', String(100)),
    schema="customer_schema"
)

此传统呈现模式将假定“customer_schema.account”不会被 SQL 语句的所有部分接受,如下所示:

>>> eng = create_engine("mssql+pymssql://mydsn", legacy_schema_aliasing=True)
>>> print(account_table.select().compile(eng))
SELECT  account_1.id,  account_1.info
FROM  customer_schema.account  AS  account_1 

此行为模式现在默认关闭,因为似乎没有任何作用;但是,如果传统应用程序依赖于它,则可以使用legacy_schema_aliasing参数来create_engine(),如上所示。

自版本 1.4 弃用:legacy_schema_aliasing标志现已弃用,并将在将来的版本中删除。 ## 聚集索引支持

MSSQL 方言支持通过mssql_clustered选项生成聚集索引(和主键)。此选项适用于IndexUniqueConstraintPrimaryKeyConstraint。对于索引,此选项可以与mssql_columnstore结合使用以创建聚集列存储索引。

要生成聚集索引:

Index("my_index", table.c.x, mssql_clustered=True)

将索引呈现为CREATE CLUSTERED INDEX my_index ON table (x)

要生成聚集主键,请使用:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x", "y", mssql_clustered=True))

这将例如呈现表为:

CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL,
                       PRIMARY KEY CLUSTERED (x, y))

类似地,我们可以使用以下方式生成��集唯一约束:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x"),
      UniqueConstraint("y", mssql_clustered=True),
      )

要显式请求非聚集主键(例如,当需要单独的聚集索引时),请使用:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x", "y", mssql_clustered=False))

这将例如呈现表为:

CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL,
                       PRIMARY KEY NONCLUSTERED (x, y))

列存储索引支持

MSSQL 方言通过 mssql_columnstore 选项支持列存储索引。此选项适用于 Index。它可以与 mssql_clustered 选项结合使用以创建聚集列存储索引。

生成列存储索引:

Index("my_index", table.c.x, mssql_columnstore=True)

渲染索引为 CREATE COLUMNSTORE INDEX my_index ON table (x)

要生成聚集列存储索引,请不提供列:

idx = Index("my_index", mssql_clustered=True, mssql_columnstore=True)
# required to associate the index with the table
table.append_constraint(idx)

上述将索引渲染为 CREATE CLUSTERED COLUMNSTORE INDEX my_index ON table

版本 2.0.18 中新增。

MSSQL 特定的索引选项

除了聚集外,MSSQL 方言还支持其他特殊选项用于 Index

包括

mssql_include 选项为给定的字符串名称渲染 INCLUDE(colname):

Index("my_index", table.c.x, mssql_include=['y'])

将索引渲染为 CREATE INDEX my_index ON table (x) INCLUDE (y)

过滤索引

mssql_where 选项为给定的字符串名称渲染 WHERE(condition):

Index("my_index", table.c.x, mssql_where=table.c.x > 10)

将索引渲染为 CREATE INDEX my_index ON table (x) WHERE x > 10

版本 1.3.4 中新增。

索引排序

索引排序可通过功能表达式实现,例如:

Index("my_index", table.c.x.desc())

将索引渲染为 CREATE INDEX my_index ON table (x DESC)

另请参阅

功能索引

兼容性级别

MSSQL 支持在数据库级别设置兼容性级别的概念。例如,可以在运行在 SQL2005 数据库服务器上的数据库上运行与 SQL2000 兼容的数据库。server_version_info 将始终返回数据库服务器版本信息(在本例中为 SQL2005),而不是兼容性级别信息。因此,如果在向后兼容模式下运行,SQLAlchemy 可能会尝试使用数据库服务器无法解析的 T-SQL 语句。

触发器

SQLAlchemy 默认使用 OUTPUT INSERTED 来获取通过 IDENTITY 列或其他服务器端默认生成的新主键值。MS-SQL 不允许在具有触发器的表上使用 OUTPUT INSERTED。要在每个具有触发器的 Table 上禁用 OUTPUT INSERTED 的使用,为其指定 implicit_returning=False

Table('mytable', metadata,
    Column('id', Integer, primary_key=True),
    # ...,
    implicit_returning=False
)

声明形式:

class MyClass(Base):
    # ...
    __table_args__ = {'implicit_returning':False}
```  ## 行数支持 / ORM 版本控制

SQL Server 驱动程序可能有限的能力返回从 UPDATE 或 DELETE 语句中更新的行数。

截至目前,PyODBC 驱动程序无法在使用 OUTPUT INSERTED 时返回行数。因此,之前的 SQLAlchemy 版本对于依赖于准确行数以将版本号与匹配行匹配的功能(如“ORM 版本控制”功能)存在限制。

SQLAlchemy 2.0 现在针对这些特定用例基于返回的行数手动检索“rowcount”;因此,虽然驱动程序仍然具有此限制,但 ORM 版本功能不再受其影响。从 SQLAlchemy 2.0.5 开始,ORM 版本控制已完全重新启用 pyodbc 驱动程序。

在版本 2.0.5 中更改:为 pyodbc 驱动程序恢复了 ORM 版本控制支持。之前,在 ORM 刷新期间会发出警告,说明不支持版本控制。

## 启用快照隔离

SQL Server 具有默认的事务隔离模式,它锁定整个表,并导致即使是轻度并发的应用程序也具有长时间的持有锁定和频繁的死锁。推荐为整个数据库启用快照隔离以支持现代的并发级别。这通过在 SQL 提示符下执行以下 ALTER DATABASE 命令来完成:

```py
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON

关于 SQL Server 快照隔离的背景信息,请参阅 msdn.microsoft.com/en-us/library/ms175095.aspx

SQL Server SQL 构造

对象名称 描述
try_cast(expression, type_) 为支持的后端生成一个 TRY_CAST 表达式;这是一个 CAST,对于不可转换的转换返回 NULL。
function sqlalchemy.dialects.mssql.try_cast(expression: _ColumnExpressionOrLiteralArgument[Any], type_: _TypeEngineArgument[_T]) → TryCast[_T]

为支持它的后端生成一个 TRY_CAST 表达式;这是一个 CAST,对于不可转换的转换返回 NULL。

在 SQLAlchemy 中,此结构仅由 SQL Server 方言支持,并且如果在其他包含的后端上使用,将引发 CompileError。但是,第三方后端也可能支持此结构。

提示

由于 try_cast() 起源于 SQL Server 方言,因此可以从 sqlalchemy. 以及 sqlalchemy.dialects.mssql 导入。

try_cast() 返回一个 TryCast 实例,并且通常行为类似于 Cast 结构;在 SQL 层面,CASTTRY_CAST 的区别在于 TRY_CAST 对于不可转换的表达式,如将字符串 "hi" 转换为整数值,将返回 NULL。

例如:

from sqlalchemy import select, try_cast, Numeric

stmt = select(
    try_cast(product_table.c.unit_price, Numeric(10, 4))
)

上述内容在 Microsoft SQL Server 上呈现为:

SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4))
FROM product_table

新版本 2.0.14 中:try_cast() 已从 SQL Server 方言广义化为一个可能由其他方言支持的通用结构。

SQL Server 数据类型

与所有 SQLAlchemy 方言一样,所有已知与 SQL Server 有效的大写类型都可以从顶级方言导入,无论它们是来自sqlalchemy.types还是来自本地方言:

from sqlalchemy.dialects.mssql import (
    BIGINT,
    BINARY,
    BIT,
    CHAR,
    DATE,
    DATETIME,
    DATETIME2,
    DATETIMEOFFSET,
    DECIMAL,
    DOUBLE_PRECISION,
    FLOAT,
    IMAGE,
    INTEGER,
    JSON,
    MONEY,
    NCHAR,
    NTEXT,
    NUMERIC,
    NVARCHAR,
    REAL,
    SMALLDATETIME,
    SMALLINT,
    SMALLMONEY,
    SQL_VARIANT,
    TEXT,
    TIME,
    TIMESTAMP,
    TINYINT,
    UNIQUEIDENTIFIER,
    VARBINARY,
    VARCHAR,
)

以下是特定于 SQL Server 或具有 SQL Server 特定构造参数的类型:

对象名称 描述
BIT MSSQL BIT 类型。
DATETIME2
DATETIMEOFFSET
DOUBLE_PRECISION SQL Server DOUBLE PRECISION 数据类型。
IMAGE
JSON MSSQL JSON 类型。
MONEY
NTEXT MSSQL NTEXT 类型,用于最多 2³⁰ 个字符的可变长度 Unicode 文本。
REAL SQL Server REAL 数据类型。
ROWVERSION 实现 SQL Server ROWVERSION 类型。
SMALLDATETIME
SMALLMONEY
SQL_VARIANT
TIME
TIMESTAMP 实现 SQL Server TIMESTAMP 类型。
TINYINT
UNIQUEIDENTIFIER
XML MSSQL XML 类型。
class sqlalchemy.dialects.mssql.BIT

MSSQL BIT 类型。

pyodbc 和 pymssql 都将 BIT 列的值作为 Python <class ‘bool’>返回,因此只需子类化 Boolean。

成员

init()

类签名

sqlalchemy.dialects.mssql.BIT (sqlalchemy.types.Boolean)

method __init__(create_constraint: bool = False, name: str | None = None, _create_events: bool = True, _adapted_from: SchemaType | None = None)

继承自 sqlalchemy.types.Boolean.__init__ 方法的 Boolean

构造一个布尔值。

参数:

  • create_constraint

    默认为 False。如果布尔值生成为 int/smallint,则还会在表上创建一个 CHECK 约束,确保值为 1 或 0。

    注意

    强烈建议 CHECK 约束具有显式名称,以支持模式管理问题。这可以通过设置Boolean.name参数或设置适当的命名约定来实现;有关背景信息,请参阅配置约束命名约定。

    从版本 1.4 开始更改:- 此标志现在默认为 False,表示对于非本地枚举类型不会生成 CHECK 约束。

  • name – 如果生成 CHECK 约束,请指定约束的名称。

class sqlalchemy.dialects.mssql.CHAR

SQL CHAR 类型。

类签名

sqlalchemy.dialects.mssql.CHAR (sqlalchemy.types.String)

method __init__(length: int | None = None, collation: str | None = None)

继承自 sqlalchemy.types.String.__init__ 方法的 String

创建一个持有字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列的长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 DDL 中使用length,如果包含没有长度的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 
    

    注意

    在大多数情况下,UnicodeUnicodeText 数据类型应该用于预期存储非 ASCII 数据的 Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.DATETIME2

类签名

sqlalchemy.dialects.mssql.DATETIME2 (sqlalchemy.dialects.mssql.base._DateTimeBase, sqlalchemy.types.DateTime)

class sqlalchemy.dialects.mssql.DATETIMEOFFSET

类签名

sqlalchemy.dialects.mssql.DATETIMEOFFSET (sqlalchemy.dialects.mssql.base._DateTimeBase, sqlalchemy.types.DateTime)

class sqlalchemy.dialects.mssql.DOUBLE_PRECISION

SQL Server DOUBLE PRECISION 数据类型。

新版本 2.0.11 中新增。

类签名

sqlalchemy.dialects.mssql.DOUBLE_PRECISION (sqlalchemy.types.DOUBLE_PRECISION)

class sqlalchemy.dialects.mssql.IMAGE

成员

init()

类签名

sqlalchemy.dialects.mssql.IMAGE (sqlalchemy.types.LargeBinary)

method __init__(length: int | None = None)

继承自 sqlalchemy.types.LargeBinary.__init__ 方法的 LargeBinary

构造一个 LargeBinary 类型。

参数:

length – 可选,用于 DDL 语句的列长度,用于那些接受长度的二进制类型,例如 MySQL BLOB 类型。

class sqlalchemy.dialects.mssql.JSON

MSSQL JSON 类型。

MSSQL 支持 JSON 格式的数据,自 SQL Server 2016 起。

DDL 级别的 JSON 数据类型将以 NVARCHAR(max) 形式表示数据类型,但也提供了 JSON 级别的比较函数以及 Python 强制转换行为。

自动使用 JSON 每当基础 JSON 数据类型针对 SQL Server 后端使用时。

另请参阅

JSON - 通用跨平台 JSON 数据类型的主要文档。

JSON 类型支持将 JSON 值持久化存储,以及通过将操作适配到数据库级别的 JSON_VALUEJSON_QUERY 函数来提供的核心索引操作,以支持 JSON 数据类型。

SQL Server JSON 类型在查询 JSON 对象元素时必然使用 JSON_QUERYJSON_VALUE 函数。这两个函数有一个主要限制,即它们根据要返回的对象类型是互斥的JSON_QUERY 函数返回 JSON 字典或列表,但不返回单个字符串、数值或布尔值元素;JSON_VALUE 函数返回单个字符串、数值或布尔值元素。如果它们没有针对正确的预期值使用,这两个函数都会返回 NULL 或引发错误

为了处理这个尴尬的要求,索引访问规则如下:

  1. 当从一个 JSON 中提取一个子元素,该 JSON 本身是一个 JSON 字典或列表时,应使用 Comparator.as_json() 访问器:

    stmt = select(
        data_table.c.data["some key"].as_json()
    ).where(
        data_table.c.data["some key"].as_json() == {"sub": "structure"}
    )
    
  2. 从 JSON 中提取平面布尔值、字符串、整数或浮点数的子元素时,使用以下适当的方法之一: Comparator.as_boolean(), Comparator.as_string(), Comparator.as_integer(), Comparator.as_float():

    stmt = select(
        data_table.c.data["some key"].as_string()
    ).where(
        data_table.c.data["some key"].as_string() == "some string"
    )
    

版本 1.4 中的新功能。

成员

init()

类签名

sqlalchemy.dialects.mssql.JSON (sqlalchemy.types.JSON)

method __init__(none_as_null: bool = False)

继承自 JSON 方法 sqlalchemy.types.JSON.__init__

构造一个 JSON 类型。

参数:

none_as_null=False

如果为 True,则将值None持久化为 SQL NULL 值,而不是null的 JSON 编码。注意,当此标志为 False 时,null() 构造仍然可以用于持久化 NULL 值,可以直接将其作为参数值传递,该值由 JSON 类型特别解释为 SQL NULL:

from sqlalchemy import null
conn.execute(table.insert(), {"data": null()})

注意

JSON.none_as_null 不适用于传递给 Column.defaultColumn.server_default 的值;这些参数的传递值为 None 意味着“没有默认值”。

此外,当用于 SQL 比较表达式时,Python 值 None 仍然指代 SQL null,而不是 JSON NULL。JSON.none_as_null 标志明确指的是在 INSERT 或 UPDATE 语句中值的持久化JSON.NULL 值应用于希望与 JSON null 进行比较的 SQL 表达式。

另请参阅

JSON.NULL

class sqlalchemy.dialects.mssql.MONEY

类签名

sqlalchemy.dialects.mssql.MONEY (sqlalchemy.types.TypeEngine)

class sqlalchemy.dialects.mssql.NCHAR

SQL NCHAR 类型。

类签名

sqlalchemy.dialects.mssql.NCHAR (sqlalchemy.types.Unicode)

method __init__(length: int | None = None, collation: str | None = None)

继承自 sqlalchemy.types.String.__init__ 方法

创建一个保存字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.NTEXT

MSSQL NTEXT 类型,用于最多 2³⁰ 个字符的可变长度 Unicode 文本。

成员

init()

类签名

sqlalchemy.dialects.mssql.NTEXT (sqlalchemy.types.UnicodeText)

method __init__(length: int | None = None, collation: str | None = None)

继承自 sqlalchemy.types.String.__init__ 方法

创建一个保存字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的 Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.NVARCHAR

SQL NVARCHAR 类型。

类签名

sqlalchemy.dialects.mssql.NVARCHAR (sqlalchemy.types.Unicode)

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个字符串持有类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的 Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.REAL

SQL Server REAL 数据类型。

类签名

sqlalchemy.dialects.mssql.REAL (sqlalchemy.types.REAL)

class sqlalchemy.dialects.mssql.ROWVERSION

实现 SQL Server ROWVERSION 类型。

ROWVERSION 数据类型是 TIMESTAMP 数据类型的 SQL Server 同义词,但当前 SQL Server 文档建议将 ROWVERSION 用于未来的新数据类型。

ROWVERSION 数据类型不会从数据库中反映出来,返回的数据类型将是 TIMESTAMP

这是一种只读数据类型,不支持插入值。

版本 1.2 中的新功能。

另请参阅

TIMESTAMP

成员

init()

类签名

sqlalchemy.dialects.mssql.ROWVERSIONsqlalchemy.dialects.mssql.base.TIMESTAMP

method __init__(convert_int=False)

TIMESTAMP sqlalchemy.dialects.mssql.base.TIMESTAMP.__init__ 方法继承

构造一个 TIMESTAMP 或 ROWVERSION 类型。

参数:

convert_int – 如果为 True,则在读取时将二进制整数值转换为整数。

新版本 1.2。

class sqlalchemy.dialects.mssql.SMALLDATETIME

成员

init()

类签名

sqlalchemy.dialects.mssql.SMALLDATETIMEsqlalchemy.dialects.mssql.base._DateTimeBasesqlalchemy.types.DateTime

method __init__(timezone: bool = False)

DateTime sqlalchemy.types.DateTime.__init__ 方法继承

构造一个新的DateTime

参数:

时区 – 布尔值。指示日期时间类型是否应在仅在基础日期/时间持有类型上可用时启用时区支持。建议在使用此标志时直接使用TIMESTAMP数据类型,因为一些数据库包括与时区功能的 TIMESTAMP 数据类型不同的单独的通用日期/时间持有类型,如 Oracle。

class sqlalchemy.dialects.mssql.SMALLMONEY

类签名

sqlalchemy.dialects.mssql.SMALLMONEYsqlalchemy.types.TypeEngine

class sqlalchemy.dialects.mssql.SQL_VARIANT

类签名

sqlalchemy.dialects.mssql.SQL_VARIANTsqlalchemy.types.TypeEngine

class sqlalchemy.dialects.mssql.TEXT

SQL TEXT 类型。

类签名

sqlalchemy.dialects.mssql.TEXTsqlalchemy.types.Text

method __init__(length: int | None = None, collation: str | None = None)

String sqlalchemy.types.String.__init__ 方法继承

创建一个字符串持有类型。

参数:

  • length – 可选项,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,可以安全地省略。某些数据库可能需要在 DDL 中使用长度,并且如果包含一个没有长度的VARCHAR,则在发出CREATE TABLEDDL 时会引发异常。值是作为字节还是字符解释的是特定于数据库的。

  • 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库中使用正确的类型。

class sqlalchemy.dialects.mssql.TIME

类签名

sqlalchemy.dialects.mssql.TIMEsqlalchemy.types.TIME

class sqlalchemy.dialects.mssql.TIMESTAMP

实现 SQL Server TIMESTAMP 类型。

请注意,这与 SQL 标准 TIMESTAMP 类型完全不同,SQL Server 不支持该类型。它是一个只读数据类型,不支持插入值。

新功能在版本 1.2 中引入。

另请参阅

ROWVERSION

成员

init()

类签名

sqlalchemy.dialects.mssql.TIMESTAMPsqlalchemy.types._Binary

method __init__(convert_int=False)

构造 TIMESTAMP 或 ROWVERSION 类型。

参数:

convert_int – 如果为 True,则二进制整数值将在读取时转换为整数。

新功能在版本 1.2 中引入。

class sqlalchemy.dialects.mssql.TINYINT

类签名

sqlalchemy.dialects.mssql.TINYINTsqlalchemy.types.Integer

class sqlalchemy.dialects.mssql.UNIQUEIDENTIFIER

成员

init()

类签名

sqlalchemy.dialects.mssql.UNIQUEIDENTIFIERsqlalchemy.types.Uuid

method __init__(as_uuid: bool = True)

构造 UNIQUEIDENTIFIER 类型。

参数:

as_uuid=True

如果为 True,则值将被解释为 Python uuid 对象,通过 DBAPI 转换为/从字符串。

class sqlalchemy.dialects.mssql.VARBINARY

MSSQL VARBINARY 类型。

该类型为核心的VARBINARY类型添加了额外的功能,包括“deprecate_large_types”模式,其中会渲染VARBINARY(max)或 IMAGE,以及 SQL Server 的FILESTREAM选项。

另请参见

大型文本/二进制类型弃用

类签名

sqlalchemy.dialects.mssql.VARBINARY (sqlalchemy.types.VARBINARY, sqlalchemy.types.LargeBinary)

method __init__(length=None, filestream=False)

构造一个 VARBINARY 类型。

参数:

  • length – 可选参数,在 DDL 语句中用于列长度,用于那些接受长度参数的二进制类型,比如 MySQL 的 BLOB 类型。

  • filestream=False

    如果为 True,在表定义中会渲染FILESTREAM关键字。在这种情况下,length必须为None或者'max'

    新于 1.4.31 版本。

class sqlalchemy.dialects.mssql.VARCHAR

SQL VARCHAR 类型。

类签名

sqlalchemy.dialects.mssql.VARCHAR (sqlalchemy.types.String)

method __init__(length: int | None = None, collation: str | None = None)

String sqlalchemy.types.String.__init__ 方法继承

创建一个保存字符串的类型。

参数:

  • length – 可选参数,在 DDL 和 CAST 表达式中用于列长度。如果不会发出CREATE TABLE,可以安全地省略。某些数据库可能需要 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。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.XML

MSSQL XML 类型。

这是一个占位符类型,用于反射目的,不包括任何 Python 端数据类型支持。它也不支持额外的参数,比如“CONTENT”、“DOCUMENT”、“xml_schema_collection”。

成员

init()

类签名

sqlalchemy.dialects.mssql.XML (sqlalchemy.types.Text)

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选项,用于 DDL 和 CAST 表达式中列的长度。如果不会发出CREATE TABLE语句,则可以安全地省略。某些数据库可能需要在 DDL 中使用长度,并且如果包括了没有长度的VARCHAR,则在发出CREATE TABLEDDL 时会引发异常。值是按字节还是按字符解释,取决于数据库。

  • 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

PyODBC

通过 PyODBC 驱动程序支持 Microsoft SQL Server 数据库。

DBAPI

PyODBC 的文档和下载信息(如果适用)可在以下网址获取:pypi.org/project/pyodbc/

连接

连接字符串:

mssql+pyodbc://<username>:<password>@<dsnname>

连接到 PyODBC

此处的 URL 将被翻译为 PyODBC 连接字符串,详见ConnectionStrings

DSN 连接

ODBC 中的 DSN 连接意味着在客户端机器上配置了预先存在的 ODBC 数据源。然后,应用程序指定此数据源的名称,其中包括诸如正在使用的特定 ODBC 驱动程序以及数据库的网络地址等细节。假设在客户端上配置了数据源,则基本的基于 DSN 的连接如下所示:

engine = create_engine("mssql+pyodbc://scott:tiger@some_dsn")

将上述内容传递给 PyODBC 的连接字符串如下:

DSN=some_dsn;UID=scott;PWD=tiger

如果省略了用户名和密码,则 DSN 表单还将向 ODBC 字符串添加Trusted_Connection=yes指令。

主机名连接

PyODBC 也支持基于主机名的连接。这通常比 DSN 更容易使用,并且具有另一个优势,即可以在 URL 中本地指定要连接到的特定数据库名称,而不是将其固定为数据源配置的一部分。

在使用主机名连接时,还必须在 URL 的查询参数中指定驱动程序名称。由于这些名称通常包含空格,因此必须对名称进行 URL 编码,这意味着使用加号代替空格:

engine = create_engine("mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=ODBC+Driver+17+for+SQL+Server")

driver关键字对于 pyodbc 方言非常重要,必须以小写形式指定。

查询字符串中传递的任何其他名称都将通过 pyodbc 连接字符串传递,例如 authenticationTrustServerCertificate 等。 多个关键字参数必须用与号(&)分隔;这些参数在生成 pyodbc 连接字符串时将被转换为分号:

e = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
    "&authentication=ActiveDirectoryIntegrated"
)

可以使用 URL 构造相等的 URL:

from sqlalchemy.engine import URL
connection_url = URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssql2017",
    port=1433,
    database="test",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "TrustServerCertificate": "yes",
        "authentication": "ActiveDirectoryIntegrated",
    },
)

通过确切的 Pyodbc 字符串传递

PyODBC 连接字符串也可以直接以 pyodbc 的格式发送,如PyODBC 文档中所述,使用参数 odbc_connectURL 对象可以帮助简化此过程:

from sqlalchemy.engine import URL
connection_string = "DRIVER={SQL Server Native Client 10.0};SERVER=dagger;DATABASE=test;UID=user;PWD=password"
connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})

engine = create_engine(connection_url)

使用访问令牌连接到数据库

一些数据库服务器仅允许使用访问令牌进行登录。 例如,SQL Server 允许使用 Azure Active Directory 令牌连接到数据库。 这需要使用 azure-identity 库创建凭据对象。 关于身份验证步骤的更多信息可以在 Microsoft 文档中找到。

获得引擎后,每次请求连接都需要将凭据发送到 pyodbc.connect。 一种方法是在引擎上设置事件侦听器,该事件侦听器将凭据令牌添加到方言的连接调用中。 关于这一点的更多讨论可以在 生成动态身份验证令牌中找到。 尤其对于 SQL Server,这将作为由 Microsoft 描述的 ODBC 连接属性传递的数据结构

下面的代码片段将创建一个引擎,该引擎使用 Azure 凭据连接到 Azure SQL 数据库:

import struct
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL
from azure import identity

SQL_COPT_SS_ACCESS_TOKEN = 1256  # Connection option for access tokens, as defined in msodbcsql.h
TOKEN_URL = "https://database.windows.net/"  # The token URL for any Azure SQL database

connection_string = "mssql+pyodbc://@my-server.database.windows.net/myDb?driver=ODBC+Driver+17+for+SQL+Server"

engine = create_engine(connection_string)

azure_credentials = identity.DefaultAzureCredential()

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    # remove the "Trusted_Connection" parameter that SQLAlchemy adds
    cargs[0] = cargs[0].replace(";Trusted_Connection=Yes", "")

    # create token credential
    raw_token = azure_credentials.get_token(TOKEN_URL).token.encode("utf-16-le")
    token_struct = struct.pack(f"<I{len(raw_token)}s", len(raw_token), raw_token)

    # apply it to keyword arguments
    cparams["attrs_before"] = {SQL_COPT_SS_ACCESS_TOKEN: token_struct}

提示

当没有用户名或密码时,SQLAlchemy pyodbc 方言当前会添加 Trusted_Connection 令牌。 根据 Microsoft 的用于 Azure 访问令牌的文档,当使用访问令牌时,连接字符串不得包含 UIDPWDAuthenticationTrusted_Connection 参数,需要将其删除。 #### 在 Azure Synapse Analytics 上避免事务相关的异常

Azure Synapse Analytics 在事务处理方面与普通 SQL Server 有显着差异;在某些情况下,Synapse 事务中的错误可能导致服务器端任意终止,从而导致 DBAPI 的 .rollback() 方法 (以及 .commit()) 失败。该问题阻止了允许 .rollback() 在没有事务存在时静默通过的常规 DBAPI 合同,因为驱动程序不期望出现此条件。此故障的症状是,在某些操作失败后尝试发出 .rollback() 后,异常消息类似于‘No corresponding transaction found. (111214)’。

可以通过向 SQL Server 方言传递 ignore_no_transaction_on_rollback=True 参数来处理此特定情况,方法是通过create_engine() 函数如下所示:

engine = create_engine(connection_url, ignore_no_transaction_on_rollback=True)

使用上述参数,方言将捕获在 connection.rollback() 期间引发的 ProgrammingError 异常,并在错误消息中包含代码 111214 时发出警告,但不会引发异常。

版本 1.4.40 中的新功能:添加了 ignore_no_transaction_on_rollback=True 参数。

为 Azure SQL 数据仓库 (DW) 连接启用自动提交

Azure SQL 数据仓库不支持事务,这可能会导致 SQLAlchemy 的“autobegin”(以及隐式提交/回滚)行为出现问题。我们可以通过在 pyodbc 和 engine 级别启用自动提交来避免这些问题:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="dw.azure.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 17 for SQL Server",
        "autocommit": "True",
    },
)

engine = create_engine(connection_url).execution_options(
    isolation_level="AUTOCOMMIT"
)

避免将大字符串参数发送为 TEXT/NTEXT

出于历史原因,默认情况下,Microsoft 的 SQL Server ODBC 驱动程序将长字符串参数(大于 4000 个 SBCS 字符或 2000 个 Unicode 字符)发送为 TEXT/NTEXT 值。多年来,TEXT 和 NTEXT 已经被弃用,并且开始在新版本的 SQL_Server/Azure 中引起兼容性问题。例如,参见此问题

从 ODBC 驱动程序 18 开始,我们可以通过 LongAsMax=Yes 连接字符串参数覆盖传统行为,并将长字符串作为 varchar(max)/nvarchar(max) 传递:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssqlserver.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "LongAsMax": "Yes",
    },
)

Pyodbc 连接池 / 连接关闭行为

PyODBC 默认使用内部连接池,这意味着连接的生命周期比在 SQLAlchemy 本身中更长。由于 SQLAlchemy 有自己的连接池行为,通常最好禁用此行为。此行为只能在创建任何连接之前在 PyODBC 模块级别全局禁用:

import pyodbc

pyodbc.pooling = False

# don't use the engine before pooling is set to False
engine = create_engine("mssql+pyodbc://user:pass@dsn")

如果将此变量保留在默认值 True应用程序将继续保持活动数据库连接,即使 SQLAlchemy 引擎本身完全丢弃连接或引擎被处理掉。

另请参阅

连接池 - 在 PyODBC 文档中。

驱动程序 / Unicode 支持

PyODBC 最适合与微软 ODBC 驱动程序一起使用,特别是在 Python 2 和 Python 3 上都支持 Unicode 的领域。

不建议在 Linux 或 OSX 上使用 FreeTDS ODBC 驱动程序与 PyODBC 一起使用;在这个领域,包括在微软为 Linux 和 OSX 提供 ODBC 驱动程序之前,历史上存在许多与 Unicode 相关的问题。现在微软为所有平台提供驱动程序,对于 PyODBC 支持,建议使用这些驱动程序。FreeTDS 仍然适用于非 ODBC 驱动程序,例如 pymssql,在那里它的工作非常出色。

行计数支持

至于 Pyodbc 与 SQLAlchemy ORM 的“版本化行”功能之前的限制,在 SQLAlchemy 2.0.5 版中已经解决。请参阅 Rowcount Support / ORM Versioning 中的说明。

快速执行多次模式

PyODBC 驱动程序包括对执行 DBAPI executemany() 调用时大大减少往返次数的“快速执行多次”模式的支持,当使用微软 ODBC 驱动程序时,对于内存中适合的有限大小批次。通过在 DBAPI 游标上设置属性 .fast_executemany 来启用此功能,当要使用 executemany 调用时。SQLAlchemy PyODBC SQL Server 方言通过将 fast_executemany 参数传递给 create_engine() 来支持此参数,仅当使用微软 ODBC 驱动程序时

engine = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?driver=ODBC+Driver+17+for+SQL+Server",
    fast_executemany=True)

从版本 2.0.9 开始更改:- fast_executemany 参数现在具有其预期的效果,这使得 PyODBC 功能在执行具有多个参数集的所有 INSERT 语句时生效,不包括 RETURNING。之前,SQLAlchemy 2.0 的 insertmanyvalues 功能通常会导致即使指定了,也大多数情况下不使用fast_executemany

1.3 版中的新功能。

另请参阅

快速执行多次 - 在 github ### 设置输入大小支持

从版本 2.0 开始,pyodbc cursor.setinputsizes() 方法用于所有语句执行,除了当 fast_executemany=True 时,不支持cursor.executemany()调用(假设 insertmanyvalues 已启用,“fastexecutemany”不管怎样都不会对 INSERT 语句产生影响)。

通过将 use_setinputsizes=False 传递给 create_engine() 可以禁用cursor.setinputsizes()的使用。

use_setinputsizes 保持默认值 True 时,可以通过使用 DialectEvents.do_setinputsizes() 钩子来自定义传递给 cursor.setinputsizes() 的每种类型符号。请参阅该方法以获取用法示例。

从 2.0 版本开始更改:mssql+pyodbc 方言现在默认为在所有语句执行中使用use_setinputsizes=True,但 fast_executemany=True 时除外,快速执行多次cursor.executemany() 调用。该行为可以通过将 use_setinputsizes=False 传递给 create_engine() 来关闭。 ## pymssql

通过 pymssql 驱动程序支持 Microsoft SQL Server 数据库。

连接

连接字符串:

mssql+pymssql://<username>:<password>@<freetds_name>/?charset=utf8

pymssql 是一个提供围绕 FreeTDS 的 Python DBAPI 接口的 Python 模块。

从 2.0.5 版本开始更改:pymssql 已恢复到 SQLAlchemy 的持续集成测试 ## aioodbc

通过 aioodbc 驱动程序支持 Microsoft SQL Server 数据库。

DBAPI

aioodbc 的文档和下载信息(如果适用)可在此处获取:pypi.org/project/aioodbc/

连接

连接字符串:

mssql+aioodbc://<username>:<password>@<dsnname>

以 asyncio 样式支持 SQL Server 数据库,使用 aioodbc 驱动程序,它本身是 pyodbc 的线程包装器。

从 2.0.23 版本开始新增:添加了在 pyodbc 和通用 aio* 方言架构之上构建的 mssql+aioodbc 方言。

使用特殊的 asyncio 中介层,aioodbc 方言可用作 SQLAlchemy asyncio 扩展包的后端。

该驱动程序的大多数行为和注意事项与在 SQL Server 上使用的 pyodbc 方言相同;有关一般背景,请参阅 PyODBC。

该方言通常仅应与 create_async_engine() 引擎创建函数一起使用;否则,连接样式与在 pyodbc 部分文档中记录的相同:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(
    "mssql+aioodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
)

支持 Microsoft SQL Server 数据库。

以下表格总结了数据库发布版本的当前支持级别。

支持的 Microsoft SQL Server 版本

支持类型 版本
在 CI 中进行全面测试 2017
普通支持 2012+
尽力而为 2005+

DBAPI 支持

提供以下方言/DBAPI 选项。请参阅各个 DBAPI 部分以获取连接信息。

  • PyODBC

  • pymssql

  • aioodbc

外部方言

除了具有本地 SQLAlchemy 支持的上述 DBAPI 层之外,还有用于其他与 SQL Server 兼容的 DBAPI 层的第三方方言。请参阅 方言 页面上的“外部方言”列表。

自动递增行为 / IDENTITY 列

SQL Server 使用IDENTITY构造提供所谓的“自动增量”行为,该构造可以放置在表中的任何单个整数列上。SQLAlchemy 将IDENTITY考虑在其整数主键列的默认“autoincrement”行为中,该行为在Column.autoincrement中描述。这意味着默认情况下,Table中的第一个整数主键列将被视为标识列 - 除非它与Sequence相关联 - 并且将生成 DDL 如下:

from sqlalchemy import Table, MetaData, Column, Integer

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True),
        Column('x', Integer))
m.create_all(engine)

上述示例将生成 DDL 如下:

CREATE  TABLE  t  (
  id  INTEGER  NOT  NULL  IDENTITY,
  x  INTEGER  NULL,
  PRIMARY  KEY  (id)
)

对于不希望使用此默认生成的IDENTITY的情况,在第一个整数主键列上指定Column.autoincrement标志为False

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True, autoincrement=False),
        Column('x', Integer))
m.create_all(engine)

要将IDENTITY关键字添加到非主键列,请在所需的Column对象上指定Column.autoincrement标志为True,并确保在任何整数主键列上将Column.autoincrement设置为False

m = MetaData()
t = Table('t', m,
        Column('id', Integer, primary_key=True, autoincrement=False),
        Column('x', Integer, autoincrement=True))
m.create_all(engine)

自版本 1.4 更改:在Column中添加了Identity构造,用于指定IDENTITY的起始值和增量参数。这些参数取代了使用Sequence对象来指定这些值。

自版本 1.4 弃用:Columnmssql_identity_startmssql_identity_increment参数已弃用,应该用Identity对象替换。指定两种配置IDENTITY的方式将导致编译错误。这些选项也不再作为Inspector.get_columns()dialect_options键的一部分返回。请改为使用identity键中的信息。

自版本 1.3 起弃用:使用Sequence指定 IDENTITY 特性已被弃用,并将在将来的版本中删除。请使用Identity对象参数Identity.startIdentity.increment

从版本 1.4 开始更改:移除了使用Sequence对象修改 IDENTITY 特性的能力。现在,Sequence对象仅操作真正的 T-SQL SEQUENCE 类型。

注意

表上只能有一个 IDENTITY 列。当使用autoincrement=True启用 IDENTITY 关键字时,SQLAlchemy 不会阻止多个列同时指定该选项。相反,SQL Server 数据库将拒绝CREATE TABLE语句。

注意

尝试为标记为 IDENTITY 的列提供值的 INSERT 语句将被 SQL Server 拒绝。为了接受该值,必须启用会话级选项“SET IDENTITY_INSERT”。当使用核心Insert构造时,SQLAlchemy SQL Server 方言将在执行指定 IDENTITY 列的值时自动执行此操作;如果执行为 IDENTITY 列指定了一个值,则“IDENTITY_INSERT”选项将在该语句调用的范围内启用。然而,这种情况的性能不高,不应该依赖于常规使用。如果表实际上不需要 IDENTITY 行为在其整数主键列中,创建表时应禁用该关键字,方法是确保autoincrement=False被设置。

控制“开始”和“增量”

通过将参数Identity.startIdentity.increment传递给Identity对象提供了对“开始”和“增量”值的特定控制:

from sqlalchemy import Table, Integer, Column, Identity

test = Table(
    'test', metadata,
    Column(
        'id',
        Integer,
        primary_key=True,
        Identity(start=100, increment=10)
    ),
    Column('name', String(20))
)

上述Table对象的 CREATE TABLE 将是:

CREATE  TABLE  test  (
  id  INTEGER  NOT  NULL  IDENTITY(100,10)  PRIMARY  KEY,
  name  VARCHAR(20)  NULL,
  )

注意

Identity对象支持许多其他参数,除了startincrement之外。这些参数不受 SQL Server 支持,在生成 CREATE TABLE ddl 时将被忽略。

版本 1.3.19 中的更改:在 SQL Server 下,现在使用 Identity 对象来影响 ColumnIDENTITY 生成器。之前使用的是 Sequence 对象。由于 SQL Server 现在支持将实际序列作为一个独立的构造,因此 Sequence 将从 SQLAlchemy 版本 1.4 开始以正常方式运作。

使用非整数数值类型的 IDENTITY

SQL Server 还允许将 IDENTITY 用于 NUMERIC 列。为了在 SQLAlchemy 中平滑实现这种模式,在列的主要数据类型应保持为 Integer,但是可以使用 TypeEngine.with_variant() 来指定部署到 SQL Server 数据库的底层实现类型为 Numeric

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Numeric
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(Numeric(10, 0), "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

在上面的示例中,Integer().with_variant() 提供了明确的使用信息,准确描述了代码的意图。将 autoincrement 仅适用于 Integer 的一般限制建立在元数据级别而不是每个方言级别。

使用上述模式时,从插入行返回的主键标识符(也是将分配给类似于上面的 TestTable 的 ORM 对象的值)将是 Decimal() 的实例,而不是使用 SQL Server 时的 int。通过将 False 传递给 Numeric.asdecimal,可以将 Numeric 类型的数值返回类型更改为浮点数。要将上述 Numeric(10, 0) 的返回类型规范化为返回 Python 整数(在 Python 3 中也支持“长”整数值),请使用 TypeDecorator 如下所示:

from sqlalchemy import TypeDecorator

class NumericAsInteger(TypeDecorator):
  '''normalize floating point return values into ints'''

    impl = Numeric(10, 0, asdecimal=False)
    cache_ok = True

    def process_result_value(self, value, dialect):
        if value is not None:
            value = int(value)
        return value

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(NumericAsInteger, "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

插入行为

在 INSERT 时处理 IDENTITY 列涉及两个关键技术。最常见的是能够获取给定 IDENTITY 列的“最后插入值”,SQLAlchemy 在许多情况下都会隐式执行这个过程,最重要的是在 ORM 中。

获取此值的过程有几种变体:

  • 在绝大多数情况下,在 SQL Server 上与 INSERT 语句一起使用 RETURNING 以获取新生成的主键值:

    INSERT  INTO  t  (x)  OUTPUT  inserted.id  VALUES  (?)
    

    从 SQLAlchemy 2.0 开始,默认还使用 INSERT 语句的“插入多个值”行为功能来优化多行 INSERT 语句;对于 SQL Server,该功能适用于 RETURNING 和非 RETURNING INSERT 语句。

    从版本 2.0.10 开始更改:由于行排序问题,SQLAlchemy 版本 2.0.9 暂时禁用了 SQL Server 的 INSERT 语句的“插入多个值”行为功能。从 2.0.10 开始,该功能已重新启用,并对工作单元对 RETURNING 的排序要求进行了特殊处理。

  • RETURNING不可用或通过implicit_returning=False禁用时,将使用scope_identity()函数或@@identity变量;后端的行为各不相同:

    • 使用 PyODBC 时,短语; select scope_identity()将被附加到插入语句的末尾;为了接收值,将获取第二个结果集。给定一个表如下:

      t = Table(
          't',
          metadata,
          Column('id', Integer, primary_key=True),
          Column('x', Integer),
          implicit_returning=False
      )
      

      插入操作看起来像是:

      INSERT  INTO  t  (x)  VALUES  (?);  select  scope_identity()
      
    • 其他方言,如 pymssql,在 INSERT 语句后调用SELECT scope_identity() AS lastrowid。如果将标志use_scope_identity=False传递给create_engine(),则将改为使用语句SELECT @@identity AS lastrowid

包含IDENTITY列的表将禁止明确引用标识列的插入语句。SQLAlchemy 方言将检测到当使用核心insert()构造创建的 INSERT 构造引用标识列时(而不是普通的字符串 SQL),在这种情况下,将在插入语句执行之前发出SET IDENTITY_INSERT ON,并在执行后发出SET IDENTITY_INSERT OFF。给定此示例:

m = MetaData()
t = Table('t', m, Column('id', Integer, primary_key=True),
                Column('x', Integer))
m.create_all(engine)

with engine.begin() as conn:
    conn.execute(t.insert(), {'id': 1, 'x':1}, {'id':2, 'x':2})

上述列将使用 IDENTITY 创建,但我们发出的 INSERT 语句指定了显式值。在回显输出中,我们可以看到 SQLAlchemy 如何处理这个问题:

CREATE  TABLE  t  (
  id  INTEGER  NOT  NULL  IDENTITY(1,1),
  x  INTEGER  NULL,
  PRIMARY  KEY  (id)
)

COMMIT
SET  IDENTITY_INSERT  t  ON
INSERT  INTO  t  (id,  x)  VALUES  (?,  ?)
((1,  1),  (2,  2))
SET  IDENTITY_INSERT  t  OFF
COMMIT

这是一个适用于测试和批量插入场景的辅助用例。

控制“开始”和“增量”

使用传递给Identity对象的Identity.startIdentity.increment参数提供对IDENTITY生成器的“开始”和“增量”值的特定控制:

from sqlalchemy import Table, Integer, Column, Identity

test = Table(
    'test', metadata,
    Column(
        'id',
        Integer,
        primary_key=True,
        Identity(start=100, increment=10)
    ),
    Column('name', String(20))
)

上述Table对象的 CREATE TABLE 将是:

CREATE  TABLE  test  (
  id  INTEGER  NOT  NULL  IDENTITY(100,10)  PRIMARY  KEY,
  name  VARCHAR(20)  NULL,
  )

注意

Identity对象除了startincrement之外还支持许多其他参数。这些参数在 SQL Server 中不受支持,在生成 CREATE TABLE ddl 时将被忽略。

从版本 1.3.19 开始更改:Identity对象现在用于影响 SQL Server 下的ColumnIDENTITY生成器。以前,使用的是Sequence对象。由于 SQL Server 现在支持真实的序列作为单独的构造,因此从 SQLAlchemy 版本 1.4 开始,Sequence将以正常的方式运行。

使用非整数数值类型的 IDENTITY

SQL Server 还允许将IDENTITYNUMERIC列一起使用。要在 SQLAlchemy 中顺利实现此模式,列的主要数据类型应保持为Integer,但是可以使用TypeEngine.with_variant()指定部署到 SQL Server 数据库的底层实现类型为Numeric

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Numeric
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(Numeric(10, 0), "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

在上面的示例中,Integer().with_variant()提供了清晰的使用信息,准确描述了代码的意图。autoincrement仅适用于Integer的一般限制是在元数据级别而不是在每个方言级别上建立的。

当使用上述模式时,从插入行返回的主键标识符,也就是将被分配给诸如上述TestTable的 ORM 对象的值,当使用 SQL Server 时将是Decimal()的实例,而不是intNumeric类型的数值返回类型可以通过将 False 传递给Numeric.asdecimal来更改为返回浮点数。要将上述Numeric(10, 0)的返回类型规范化为返回 Python 整数(在 Python 3 中也支持“long”整数值),请使用TypeDecorator如下所示:

from sqlalchemy import TypeDecorator

class NumericAsInteger(TypeDecorator):
  '''normalize floating point return values into ints'''

    impl = Numeric(10, 0, asdecimal=False)
    cache_ok = True

    def process_result_value(self, value, dialect):
        if value is not None:
            value = int(value)
        return value

class TestTable(Base):
    __tablename__ = "test"
    id = Column(
        Integer().with_variant(NumericAsInteger, "mssql"),
        primary_key=True,
        autoincrement=True,
    )
    name = Column(String)

插入行为

在 INSERT 时处理IDENTITY列涉及两种关键技术。最常见的是能够获取给定IDENTITY列的“最后插入的值”,这是 SQLAlchemy 在许多情况下隐式执行的过程,最重要的是在 ORM 中。

获取此值的过程有几种变体:

  • 在绝大多数情况下,RETURNING 与 SQL Server 上的 INSERT 语句一起使用,以获取新生成的主键值:

    INSERT  INTO  t  (x)  OUTPUT  inserted.id  VALUES  (?)
    

    从 SQLAlchemy 2.0 开始,默认还使用“Insert Many Values” Behavior for INSERT statements 功能来优化多行 INSERT 语句;对于 SQL Server,该功能适用于 RETURNING 和非 RETURNING INSERT 语句。

    从版本 2.0.10 开始更改:由于行排序问题,SQLAlchemy 版本 2.0.9 暂时禁用了 SQL Server 的“Insert Many Values” Behavior for INSERT statements 功能。从 2.0.10 开始,该功能重新启用,并针对工作单元对 RETURNING 的排序要求进行特殊处理。

  • 当 RETURNING 不可用或通过implicit_returning=False禁用时,将使用scope_identity()函数或@@identity变量;后端的行为各不相同:

    • 使用 PyODBC 时,短语; select scope_identity()将附加到 INSERT 语句的末尾;为了接收值,将获取第二个结果集。假设有一个表:

      t = Table(
          't',
          metadata,
          Column('id', Integer, primary_key=True),
          Column('x', Integer),
          implicit_returning=False
      )
      

      一个 INSERT 看起来像:

      INSERT  INTO  t  (x)  VALUES  (?);  select  scope_identity()
      
    • 其他方言,如 pymssql,在 INSERT 语句后将调用SELECT scope_identity() AS lastrowid。如果将标志use_scope_identity=False传递给create_engine(),则将使用语句SELECT @@identity AS lastrowid

包含IDENTITY列的表将禁止引用显式标识列的 INSERT 语句。当 SQLAlchemy 方言检测到使用核心insert()构造(而不是纯字符串 SQL)创建的 INSERT 构造引用标识列时,在这种情况下,将在继续插入语句之前发出SET IDENTITY_INSERT ON,并在执行后继续发出SET IDENTITY_INSERT OFF。给出这个例子:

m = MetaData()
t = Table('t', m, Column('id', Integer, primary_key=True),
                Column('x', Integer))
m.create_all(engine)

with engine.begin() as conn:
    conn.execute(t.insert(), {'id': 1, 'x':1}, {'id':2, 'x':2})

上述列将使用 IDENTITY 创建,但我们发出的 INSERT 语句指定了显式值。在回显输出中,我们可以看到 SQLAlchemy 如何处理这个问题:

CREATE  TABLE  t  (
  id  INTEGER  NOT  NULL  IDENTITY(1,1),
  x  INTEGER  NULL,
  PRIMARY  KEY  (id)
)

COMMIT
SET  IDENTITY_INSERT  t  ON
INSERT  INTO  t  (id,  x)  VALUES  (?,  ?)
((1,  1),  (2,  2))
SET  IDENTITY_INSERT  t  OFF
COMMIT

这是一个适用于测试和批量插入场景的辅助用例。

SEQUENCE 支持

Sequence对象创建“真实”序列,即CREATE SEQUENCE

>>> from sqlalchemy import Sequence
>>> from sqlalchemy.schema import CreateSequence
>>> from sqlalchemy.dialects import mssql
>>> print(CreateSequence(Sequence("my_seq", start=1)).compile(dialect=mssql.dialect()))
CREATE  SEQUENCE  my_seq  START  WITH  1 

对于整数主键生成,通常应优先选择 SQL Server 的IDENTITY构造而不是序列。

提示

T-SQL 的默认起始值为-2**63,而不是大多数其他 SQL 数据库中的 1。如果预期默认值是 1,则用户应明确设置Sequence.start

seq = Sequence("my_sequence", start=1)

从版本 1.4 开始:为Sequence添加了对 SQL Server 的支持

在 2.0 版本中更改:SQL Server 方言将不再隐式呈现“START WITH 1”用于CREATE SEQUENCE,这是在 1.4 版本中首次实现的行为。

VARCHAR / NVARCHAR 上的 MAX

SQL Server 支持特殊字符串“MAX”在VARCHARNVARCHAR数据类型中,以指示“可能的最大长度”。方言当前将此处理为基本类型中长度为“None”,而不是提供这些类型的特定于方言的版本,因此可以假定指定为VARCHAR(None)之类的基本类型在不使用特定于方言的类型的情况下,在多个后端上表现出“无长度”的行为。

要构建具有 MAX 长度的 SQL Server VARCHAR 或 NVARCHAR,请使用 None:

my_table = Table(
    'my_table', metadata,
    Column('my_data', VARCHAR(None)),
    Column('my_n_data', NVARCHAR(None))
)

字符串排序支持

基本字符串类型支持字符排序,由字符串参数“collation”指定:

from sqlalchemy import VARCHAR
Column('login', VARCHAR(32, collation='Latin1_General_CI_AS'))

当此列与Table关联时,该列的 CREATE TABLE 语句将产生:

login VARCHAR(32) COLLATE Latin1_General_CI_AS NULL

LIMIT/OFFSET 支持

MSSQL 从 SQL Server 2012 开始增加了对 LIMIT / OFFSET 的支持,通过“OFFSET n ROWS”和“FETCH NEXT n ROWS”子句。如果检测到 SQL Server 2012 或更高版本,则 SQLAlchemy 会自动支持这些语法。

1.4 版本中更改:增加了对 SQL Server“OFFSET n ROWS”和“FETCH NEXT n ROWS”语法的支持。

对于仅指定 LIMIT 而不指定 OFFSET 的语句,所有版本的 SQL Server 都支持 TOP 关键字。当没有 OFFSET 子句时,此语法用于所有 SQL Server 版本。例如这样的语句:

select(some_table).limit(5)

将类似于以下内容呈现:

SELECT TOP 5 col1, col2.. FROM table

对于 SQL Server 2012 之前的版本,使用 LIMIT 和 OFFSET 或仅使用 OFFSET 的语句将使用ROW_NUMBER()窗口函数呈现。例如这样的语句:

select(some_table).order_by(some_table.c.col3).limit(5).offset(10)

将类似于以下内容呈现:

SELECT anon_1.col1, anon_1.col2 FROM (SELECT col1, col2,
ROW_NUMBER() OVER (ORDER BY col3) AS
mssql_rn FROM table WHERE t.x = :x_1) AS
anon_1 WHERE mssql_rn > :param_1 AND mssql_rn <= :param_2 + :param_1

请注意,无论是使用旧版还是新版 SQL Server 语法,使用 LIMIT 和/或 OFFSET 时,语句必须也有 ORDER BY,否则会引发CompileError

DDL 注释支持

支持注释,包括对Table.commentColumn.comment等属性的 DDL 呈现,以及反映这些注释的能力,假定正在使用受支持的 SQL Server 版本。如果在首次连接时检测到不受支持的版本(例如 Azure Synapse)(基于fn_listextendedproperty SQL 函数的存在),则会禁用注释支持,包括呈现和表注释反射,因为这两个功能依赖于并非所有后端类型都可用的 SQL Server 存储过程和函数。

要强制开启或关闭注释支持,绕过自动检测,请在 create_engine() 中设置参数 supports_comments

e = create_engine("mssql+pyodbc://u:p@dsn", supports_comments=False)

新版本 2.0 中:增加了对 SQL Server 方言的表和列注释的支持,包括 DDL 生成和反射。

事务隔离级别

所有 SQL Server 方言都支持通过方言特定参数create_engine.isolation_level(由create_engine() 接受)以及传递给 Connection.execution_options()Connection.execution_options.isolation_level 参数来设置事务隔离级别。该功能通过为每个新连接发出命令 SET TRANSACTION ISOLATION LEVEL <level> 来实现。

使用 create_engine() 设置隔离级别:

engine = create_engine(
    "mssql+pyodbc://scott:tiger@ms_2008",
    isolation_level="REPEATABLE READ"
)

使用每个连接的执行选项来设置:

connection = engine.connect()
connection = connection.execution_options(
    isolation_level="READ COMMITTED"
)

isolation_level 的有效值包括:

  • AUTOCOMMIT - pyodbc / pymssql 特有

  • READ COMMITTED

  • READ UNCOMMITTED

  • REPEATABLE READ

  • SERIALIZABLE

  • SNAPSHOT - SQL Server 特有

还有更多关于隔离级别配置的选项,例如与主Engine关联的“子引擎”对象,每个对象都应用不同的隔离级别设置。请参阅设置事务隔离级别,包括 DBAPI 自动提交的讨论以获取更多背景信息。

另请参阅

设置事务隔离级别,包括 DBAPI 自动提交

连接池的临时表 / 资源重置

SQLAlchemy Engine 对象使用的 QueuePool 连接池实现包含 返回时重置 行为,当连接返回到池中时将调用 DBAPI 的.rollback() 方法。虽然此回滚会清除前一个事务使用的即时状态,但它不涵盖更广泛范围的会话级状态,包括临时表以及其他服务器状态,如准备好的语句句柄和语句缓存。一个名为sp_reset_connection的未记录的 SQL Server 程序被认为是此问题的解决方法,它将重置建立在连接上的大部分会话状态,包括临时表。

要将sp_reset_connection安装为执行返回时重置的方法,可以使用 PoolEvents.reset() 事件钩子,如下例所示。将 create_engine.pool_reset_on_return 参数设置为None,以便自定义方案可以完全替换默认行为。自定义钩子实现在任何情况下调用.rollback(),因为通常重要的是 DBAPI 自身的提交/回滚跟踪将保持与事务状态一致:

from sqlalchemy import create_engine
from sqlalchemy import event

mssql_engine = create_engine(
    "mssql+pyodbc://scott:tiger⁵HHH@mssql2017:1433/test?driver=ODBC+Driver+17+for+SQL+Server",

    # disable default reset-on-return scheme
    pool_reset_on_return=None,
)

@event.listens_for(mssql_engine, "reset")
def _reset_mssql(dbapi_connection, connection_record, reset_state):
    if not reset_state.terminate_only:
        dbapi_connection.execute("{call sys.sp_reset_connection}")

    # so that the DBAPI itself knows that the connection has been
    # reset
    dbapi_connection.rollback()

在 2.0.0b3 版中更改:为 PoolEvents.reset() 事件添加了额外的状态参数,并且还确保对所有“重置”事件进行调用,因此它适用于自定义“重置”处理程序的地方。先前使用 PoolEvents.checkin() 处理程序的方案仍然可用。

另请参阅

返回时重置 - 在 连接池 文档中

可空性

MSSQL 支持三个级别的列可空性。默认的可空性允许空值,并且在 CREATE TABLE 构造中是显式的:

name VARCHAR(20) NULL

如果指定了nullable=None,则不做任何规定。换句话说,将使用数据库配置的默认值。这将呈现为:

name VARCHAR(20)

如果nullableTrueFalse,则列将分别为NULLNOT NULL

日期/时间处理

支持 DATE 和 TIME。根据大多数 MSSQL 驱动程序的要求,绑定参数将转换为 datetime.datetime() 对象,并且如果需要的话,结果将从字符串中处理。对于 MSSQL 2005 及之前版本,不可用 DATE 和 TIME 类型 - 如果检测到低于 2008 的服务器版本,则将为这些类型发出 DDL 作为 DATETIME。

大文本/二进制类型弃用

根据 SQL Server 2012/2014 文档NTEXTTEXTIMAGE 数据类型将在将来的发布中从 SQL Server 中删除。SQLAlchemy 通常将这些类型关联到 UnicodeTextTextClauseLargeBinary 数据类型。

为了适应这一变化,方言新增了一个名为 deprecate_large_types 的新标志,该标志将根据正在使用的服务器版本的检测自动设置,如果用户未设置其他值的话。该标志的行为如下:

  • 当此标志为 True 时,UnicodeTextTextClauseLargeBinary 数据类型在用于渲染 DDL 时,将分别呈现类型 NVARCHAR(max)VARCHAR(max)VARBINARY(max)。这是此标志添加后的新行为。

  • 当此标志为 False 时,UnicodeTextTextClauseLargeBinary 数据类型在用于渲染 DDL 时,将分别呈现类型 NTEXTTEXTIMAGE。这是这些类型的长期行为。

  • 标志在建立数据库连接之前以值 None 开始。如果方言在未设置标志的情况下用于渲染 DDL,则其解释方式与 False 相同。

  • 在第一次连接时,方言会检测是否正在使用 SQL Server 2012 或更高版本;如果标志仍处于 None,则根据是否检测到 2012 或更高版本来设置为 TrueFalse

  • 当创建方言时,可以将标志设置为 TrueFalse,通常通过 create_engine() 完成:

    eng = create_engine("mssql+pymssql://user:pass@host/db",
                    deprecate_large_types=True)
    
  • Complete control over whether the “old” or “new” types are rendered is available in all SQLAlchemy versions by using the UPPERCASE type objects instead: NVARCHAR, VARCHAR, VARBINARY, TEXT, NTEXT, IMAGE will always remain fixed and always output exactly that type.

Multipart Schema Names

SQL Server schemas sometimes require multiple parts to their “schema” qualifier, that is, including the database name and owner name as separate tokens, such as mydatabase.dbo.some_table. These multipart names can be set at once using the Table.schema argument of Table:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="mydatabase.dbo"
)

When performing operations such as table or component reflection, a schema argument that contains a dot will be split into separate “database” and “owner” components in order to correctly query the SQL Server information schema tables, as these two values are stored separately. Additionally, when rendering the schema name for DDL or SQL, the two components will be quoted separately for case sensitive names and other special characters. Given an argument as below:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="MyDataBase.dbo"
)

The above schema would be rendered as [MyDataBase].dbo, and also in reflection, would be reflected using “dbo” as the owner and “MyDataBase” as the database name.

To control how the schema name is broken into database / owner, specify brackets (which in SQL Server are quoting characters) in the name. Below, the “owner” will be considered as MyDataBase.dbo and the “database” will be None:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="[MyDataBase.dbo]"
)

To individually specify both database and owner name with special characters or embedded dots, use two sets of brackets:

Table(
    "some_table", metadata,
    Column("q", String(50)),
    schema="[MyDataBase.Period].[MyOwner.Dot]"
)

Changed in version 1.2: the SQL Server dialect now treats brackets as identifier delimiters splitting the schema into separate database and owner tokens, to allow dots within either name itself.

Legacy Schema Mode

Very old versions of the MSSQL dialect introduced the behavior such that a schema-qualified table would be auto-aliased when used in a SELECT statement; given a table:

account_table = Table(
    'account', metadata,
    Column('id', Integer, primary_key=True),
    Column('info', String(100)),
    schema="customer_schema"
)

this legacy mode of rendering would assume that “customer_schema.account” would not be accepted by all parts of the SQL statement, as illustrated below:

>>> eng = create_engine("mssql+pymssql://mydsn", legacy_schema_aliasing=True)
>>> print(account_table.select().compile(eng))
SELECT  account_1.id,  account_1.info
FROM  customer_schema.account  AS  account_1 

此行为模式现在默认关闭,因为似乎没有任何作用;但是,如果传统应用程序依赖于它,则可以使用create_engine()中的legacy_schema_aliasing参数来使用,如上所示。

自版本 1.4 起弃用:legacy_schema_aliasing标志现已弃用,并将在将来的版本中删除。

聚集索引支持

MSSQL 方言支持通过mssql_clustered选项生成聚集索引(和主键)。此选项适用于IndexUniqueConstraintPrimaryKeyConstraint。对于索引,此选项可以与mssql_columnstore结合使用以创建聚集列存储索引。

生成一个聚集索引:

Index("my_index", table.c.x, mssql_clustered=True)

将索引渲染为CREATE CLUSTERED INDEX my_index ON table (x)

要生成一个聚集主键,请使用:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x", "y", mssql_clustered=True))

例如,将表渲染为:

CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL,
                       PRIMARY KEY CLUSTERED (x, y))

类似地,我们可以使用以下方法生成一个聚类唯一约束:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x"),
      UniqueConstraint("y", mssql_clustered=True),
      )

要明确请求非聚集主键(例如,当需要单独的聚集索引时),请使用:

Table('my_table', metadata,
      Column('x', ...),
      Column('y', ...),
      PrimaryKeyConstraint("x", "y", mssql_clustered=False))

例如,将表渲染为:

CREATE TABLE my_table (x INTEGER NOT NULL, y INTEGER NOT NULL,
                       PRIMARY KEY NONCLUSTERED (x, y))

列存储索引支持

MSSQL 方言通过mssql_columnstore选项支持列存储索引。此选项适用于Index。它可以与mssql_clustered选项结合使用以创建聚集列存储索引。

要生成列存储索引:

Index("my_index", table.c.x, mssql_columnstore=True)

将索引渲染为CREATE COLUMNSTORE INDEX my_index ON table (x)

要生成一个聚集列存储索引,请不提供列:

idx = Index("my_index", mssql_clustered=True, mssql_columnstore=True)
# required to associate the index with the table
table.append_constraint(idx)

上述将索引渲染为CREATE CLUSTERED COLUMNSTORE INDEX my_index ON table

版本 2.0.18 中的新功能。

MSSQL 特定的索引选项

除了聚类外,MSSQL 方言还支持其他特殊选项用于Index

包括

mssql_include选项为给定的字符串名称渲染 INCLUDE(colname):

Index("my_index", table.c.x, mssql_include=['y'])

将索引渲染为CREATE INDEX my_index ON table (x) INCLUDE (y)

过滤索引

mssql_where选项为给定的字符串名称渲染 WHERE(condition):

Index("my_index", table.c.x, mssql_where=table.c.x > 10)

将索引渲染为CREATE INDEX my_index ON table (x) WHERE x > 10

版本 1.3.4 中的新功能。

索引排序

索引排序可通过功能表达式获得,例如:

Index("my_index", table.c.x.desc())

将索引渲染为CREATE INDEX my_index ON table (x DESC)

另请参阅

功能性索引

包括

mssql_include选项为给定的字符串名称渲染 INCLUDE(colname):

Index("my_index", table.c.x, mssql_include=['y'])

渲染索引为CREATE INDEX my_index ON table (x) INCLUDE (y)

过滤索引

mssql_where 选项为给定的字符串名称渲染 WHERE(condition):

Index("my_index", table.c.x, mssql_where=table.c.x > 10)

渲染索引为CREATE INDEX my_index ON table (x) WHERE x > 10

在 1.3.4 版中新增。

索引排序

可以通过函数表达式实现索引排序,例如:

Index("my_index", table.c.x.desc())

渲染索引为CREATE INDEX my_index ON table (x DESC)

另请参阅

功能索引

兼容性级别

MSSQL 支持在数据库级别设置兼容性级别的概念。这允许例如,在运行于 SQL2005 数据库服务器上时运行与 SQL2000 兼容的数据库。server_version_info 将始终返回数据库服务器版本信息(在此情况下为 SQL2005),而不是兼容性级别信息。因此,如果在向后兼容模式下运行,则 SQLAlchemy 可能会尝试使用数据库服务器无法解析的 T-SQL 语句。

触发器

SQLAlchemy 默认使用 OUTPUT INSERTED 获取通过 IDENTITY 列或其他服务器端默认值生成的新主键值。MS-SQL 不允许在具有触发器的表上使用 OUTPUT INSERTED。要在每个具有触发器的 Table 上禁用 OUTPUT INSERTED 的使用,请为其指定 implicit_returning=False

Table('mytable', metadata,
    Column('id', Integer, primary_key=True),
    # ...,
    implicit_returning=False
)

声明形式:

class MyClass(Base):
    # ...
    __table_args__ = {'implicit_returning':False}

行数支持 / ORM 版本控制

SQL Server 驱动程序可能有限的能力来返回更新或删除语句所影响的行数。

截至本文撰写时,PyODBC 驱动程序无法在使用 OUTPUT INSERTED 时返回行数。因此,SQLAlchemy 的先前版本在功能上存在限制,例如依赖准确的行数来匹配版本号与匹配行的“ORM 版本控制”功能。

SQLAlchemy 2.0 现在根据返回的 RETURNING 中到达的行数手动检索这些特定用例的“行数”;因此,虽然驱动程序仍具有此限制,但 ORM 版本控制功能不再受其影响。截至 SQLAlchemy 2.0.5,已完全重新启用了 pyodbc 驱动程序的 ORM 版本控制功能。

在 2.0.5 版更改:对于 pyodbc 驱动程序,已恢复 ORM 版本控制支持。先前,ORM 刷新期间会发出警告,说明不支持版本控制。

启用快照隔离

SQL Server 具有默认的事务隔离模式,锁定整个表,并导致即使是稍微并发的应用程序也具有长时间持有的锁定和频繁的死锁。为了支持现代级别的并发性,建议为整个数据库启用快照隔离。这通过在 SQL 提示符下执行以下 ALTER DATABASE 命令来完成:

ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON

关于 SQL Server 快照隔离的背景信息,请访问 msdn.microsoft.com/en-us/library/ms175095.aspx

SQL Server SQL 构造

对象名称 描述
try_cast(expression, type_) 为支持的后端生成一个 TRY_CAST 表达式;这是一个返回 NULL 的 CAST,用于不可转换的转换。
function sqlalchemy.dialects.mssql.try_cast(expression: _ColumnExpressionOrLiteralArgument[Any], type_: _TypeEngineArgument[_T]) → TryCast[_T]

为支持的后端生成一个 TRY_CAST 表达式;这是一个返回 NULL 的 CAST,用于不可转换的转换。

在 SQLAlchemy 中,此构造仅受 SQL Server 方言支持,并且如果在其他包含的后端上使用,则会引发 CompileError。但是,第三方后端也可能支持此构造。

提示

由于 try_cast() 来源于 SQL Server 方言,因此它既可以从 sqlalchemy. 导入,也可以从 sqlalchemy.dialects.mssql 导入。

try_cast() 返回 TryCast 的实例,并且通常表现得与 Cast 构造类似;在 SQL 层面,CASTTRY_CAST 之间的区别在于 TRY_CAST 对于不可转换的表达式(例如,尝试将字符串 "hi" 转换为整数值)返回 NULL。

例如:

from sqlalchemy import select, try_cast, Numeric

stmt = select(
    try_cast(product_table.c.unit_price, Numeric(10, 4))
)

上述内容在 Microsoft SQL Server 上呈现为:

SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4))
FROM product_table

从版本 2.0.14 开始:try_cast()已从 SQL Server 方言泛化为一个通用构造,可能由其他方言支持。

SQL Server 数据类型

与所有 SQLAlchemy 方言一样,所有已知在 SQL Server 中有效的大写类型都可以从顶级方言导入,无论其来源是sqlalchemy.types 还是来自本地方言:

from sqlalchemy.dialects.mssql import (
    BIGINT,
    BINARY,
    BIT,
    CHAR,
    DATE,
    DATETIME,
    DATETIME2,
    DATETIMEOFFSET,
    DECIMAL,
    DOUBLE_PRECISION,
    FLOAT,
    IMAGE,
    INTEGER,
    JSON,
    MONEY,
    NCHAR,
    NTEXT,
    NUMERIC,
    NVARCHAR,
    REAL,
    SMALLDATETIME,
    SMALLINT,
    SMALLMONEY,
    SQL_VARIANT,
    TEXT,
    TIME,
    TIMESTAMP,
    TINYINT,
    UNIQUEIDENTIFIER,
    VARBINARY,
    VARCHAR,
)

特定于 SQL Server 或具有 SQL Server 特定构造参数的类型如下:

对象名称 描述
BIT MSSQL BIT 类型。
DATETIME2
DATETIMEOFFSET
DOUBLE_PRECISION SQL Server DOUBLE PRECISION 数据类型。
IMAGE
JSON MSSQL JSON 类型。
MONEY
NTEXT MSSQL NTEXT 类型,用于最多 2³⁰ 个字符的变长 unicode 文本。
REAL SQL Server REAL 数据类型。
ROWVERSION 实现 SQL Server ROWVERSION 类型。
SMALLDATETIME
SMALLMONEY
SQL_VARIANT
TIME
TIMESTAMP 实现 SQL Server TIMESTAMP 类型。
TINYINT
UNIQUEIDENTIFIER
XML MSSQL XML 类型。
class sqlalchemy.dialects.mssql.BIT

MSSQL BIT 类型。

pyodbc 和 pymssql 都将 BIT 列的值作为 Python <class ‘bool’> 返回,因此只需对 Boolean 进行子类化。

成员

init()

类签名

sqlalchemy.dialects.mssql.BIT (sqlalchemy.types.Boolean)

method __init__(create_constraint: bool = False, name: str | None = None, _create_events: bool = True, _adapted_from: SchemaType | None = None)

Boolean sqlalchemy.types.Boolean.__init__ 方法继承

构造一个布尔值。

参数:

  • create_constraint -

    默认为 False。如果布尔值生成为 int/smallint,还会在表上创建 CHECK 约束,以确保值为 1 或 0。

    注意

    强烈建议 CHECK 约束具有明确的名称,以支持模式管理方面的考虑。这可以通过设置 Boolean.name 参数或设置适当的命名约定来实现;请参阅 配置约束命名约定 了解背景信息。

    从版本 1.4 开始更改:- 此标志现在默认为 False,意味着对于非本地枚举类型不生成 CHECK 约束。

  • name - 如果生成 CHECK 约束,请指定约束的名称。

class sqlalchemy.dialects.mssql.CHAR

SQL CHAR 类型。

类签名

sqlalchemy.dialects.mssql.CHAR (sqlalchemy.types.String)

method __init__(length: int | None = None, collation: str | None = None)

String sqlalchemy.types.String.__init__ 方法继承

创建一个保存字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能需要用于 DDL 的 length,如果包含了没有长度的 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 
    

    注意

    在大多数情况下,应该为预计存储非 ASCII 数据的 Column 使用 UnicodeUnicodeText 数据类型。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.DATETIME2

类签名

sqlalchemy.dialects.mssql.DATETIME2sqlalchemy.dialects.mssql.base._DateTimeBasesqlalchemy.types.DateTime

class sqlalchemy.dialects.mssql.DATETIMEOFFSET

类签名

sqlalchemy.dialects.mssql.DATETIMEOFFSETsqlalchemy.dialects.mssql.base._DateTimeBasesqlalchemy.types.DateTime

class sqlalchemy.dialects.mssql.DOUBLE_PRECISION

SQL Server 的 DOUBLE PRECISION 数据类型。

新版 2.0.11 中新增。

类签名

sqlalchemy.dialects.mssql.DOUBLE_PRECISIONsqlalchemy.types.DOUBLE_PRECISION

class sqlalchemy.dialects.mssql.IMAGE

成员

init()

类签名

sqlalchemy.dialects.mssql.IMAGEsqlalchemy.types.LargeBinary

method __init__(length: int | None = None)

继承自 LargeBinary sqlalchemy.types.LargeBinary.__init__ 方法

构造一个 LargeBinary 类型。

参数:

length – 可选,用于 DDL 语句中的列长度,对于那些接受长度的二进制类型,如 MySQL 的 BLOB 类型。

class sqlalchemy.dialects.mssql.JSON

MSSQL JSON 类型。

MSSQL 支持 JSON 格式的数据,从 SQL Server 2016 开始。

在 DDL 级别上,JSON 数据类型将表示为 NVARCHAR(max),但还提供了 JSON 级别的比较函数以及 Python 强制行为。

每当基本的 JSON 数据类型用于 SQL Server 后端时,都会自动使用 JSON

另请参阅

JSON - 通用跨平台 JSON 数据类型的主要文档。

JSON 类型支持将 JSON 值持久化,同时通过调整操作以在数据库级别渲染 JSON_VALUEJSON_QUERY 函数来提供 JSON 数据类型提供的核心索引操作。

SQL Server JSON 类型在查询 JSON 对象的元素时必然使用 JSON_QUERYJSON_VALUE 函数。 这两个函数有一个主要限制,即它们基于要返回的对象类型是 互斥的JSON_QUERY 函数返回 JSON 字典或列表,而不是单个字符串、数字或布尔元素;JSON_VALUE 函数返回单个字符串、数字或布尔元素。 这两个函数都会在不使用预期正确的值时返回 NULL 或引发错误

为了处理这个尴尬的要求,索引访问规则如下:

  1. 当从 JSON 中提取的子元素本身是 JSON 字典或列表时,应使用 Comparator.as_json() 访问器:

    stmt = select(
        data_table.c.data["some key"].as_json()
    ).where(
        data_table.c.data["some key"].as_json() == {"sub": "structure"}
    )
    
  2. 当从 JSON 中提取为普通布尔值、字符串、整数或浮点数的子元素时,请使用以下适当的方法之一:Comparator.as_boolean()Comparator.as_string()Comparator.as_integer()Comparator.as_float():

    stmt = select(
        data_table.c.data["some key"].as_string()
    ).where(
        data_table.c.data["some key"].as_string() == "some string"
    )
    

版本 1.4 中的新功能。

成员

init()

类签名

sqlalchemy.dialects.mssql.JSONsqlalchemy.types.JSON

method __init__(none_as_null: bool = False)

继承自 JSONsqlalchemy.types.JSON.__init__ 方法

构造一个 JSON 类型。

参数:

none_as_null=False

如果为 True,则将值 None 持久化为 SQL NULL 值,而不是 null 的 JSON 编码。请注意,当此标志为 False 时,null() 构造仍然可以用于持久化 NULL 值,可以直接作为参数值传递,由 JSON 类型特殊解释为 SQL NULL:

from sqlalchemy import null
conn.execute(table.insert(), {"data": null()})

注意

JSON.none_as_null 不适用于传递给 Column.defaultColumn.server_default 的值;这些参数的值为 None 表示“没有默认值”。

此外,在 SQL 比较表达式中使用时,Python 值 None 仍然指的是 SQL 空值,而不是 JSON 的 NULL。JSON.none_as_null 标志明确指示了值在 INSERT 或 UPDATE 语句中的持久性JSON.NULL 值应该用于希望与 JSON null 进行比较的 SQL 表达式。

另请参阅

JSON.NULL

class sqlalchemy.dialects.mssql.MONEY

类签名

sqlalchemy.dialects.mssql.MONEY (sqlalchemy.types.TypeEngine)

class sqlalchemy.dialects.mssql.NCHAR

SQL NCHAR 类型。

类签名

sqlalchemy.dialects.mssql.NCHAR (sqlalchemy.types.Unicode)

method __init__(length: int | None = None, collation: str | None = None)

继承自 Stringsqlalchemy.types.String.__init__ 方法

创建一个保存字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能要求在 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.NTEXT

MSSQL NTEXT 类型,用于最多 2³⁰ 个字符的可变长度 Unicode 文本。

成员

init()

类签名

sqlalchemy.dialects.mssql.NTEXT (sqlalchemy.types.UnicodeText)

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选的,用于 DDL 和 CAST 表达式中的列的长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 DDL 中使用length,如果包含一个没有长度的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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.NVARCHAR

SQL NVARCHAR 类型。

类签名

sqlalchemy.dialects.mssql.NVARCHAR (sqlalchemy.types.Unicode)

method __init__(length: int | None = None, collation: str | None = None)

继承自 String sqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选的,用于 DDL 和 CAST 表达式中的列的长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 DDL 中使用length,如果包含一个没有长度的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 
    

    注意

    在大多数情况下,应该使用 Column 期望存储非 ASCII 数据的 UnicodeUnicodeText 数据类型。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.REAL

SQL Server REAL 数据类型。

类签名

sqlalchemy.dialects.mssql.REAL (sqlalchemy.types.REAL)。

class sqlalchemy.dialects.mssql.ROWVERSION

实现 SQL Server ROWVERSION 类型。

ROWVERSION 数据类型是 SQL Server TIMESTAMP 数据类型的同义词,但当前的 SQL Server 文档建议将 ROWVERSION 用于未来新的数据类型。

ROWVERSION 数据类型 不会 作为自身反映(例如自省)从数据库中返回;返回的数据类型将是 TIMESTAMP

这是一个只读数据类型,不支持插入值。

新版本 1.2 中的新增功能。

另请参阅

TIMESTAMP

成员

init()

类签名

sqlalchemy.dialects.mssql.ROWVERSION (sqlalchemy.dialects.mssql.base.TIMESTAMP)。

method __init__(convert_int=False)

继承自 TIMESTAMP sqlalchemy.dialects.mssql.base.TIMESTAMP.__init__ 方法

构造一个 TIMESTAMP 或 ROWVERSION 类型。

参数:

convert_int – 如果为 True,则在读取时将二进制整数值转换为整数。

新版本 1.2 中的新增功能。

class sqlalchemy.dialects.mssql.SMALLDATETIME

成员

init()

类签名

sqlalchemy.dialects.mssql.SMALLDATETIME (sqlalchemy.dialects.mssql.base._DateTimeBase, sqlalchemy.types.DateTime)。

method __init__(timezone: bool = False)

继承自 DateTime sqlalchemy.types.DateTime.__init__ 方法

构造一个新的 DateTime

参数:

timezone – 布尔值。指示日期/时间类型是否应启用时区支持,仅当基本日期/时间持有类型可用时。建议在使用此标志时直接使用 TIMESTAMP 数据类型,因为某些数据库包含与支持时区的 TIMESTAMP 数据类型不同的单独的通用日期/时间持有类型,例如 Oracle。

class sqlalchemy.dialects.mssql.SMALLMONEY

类签名

sqlalchemy.dialects.mssql.SMALLMONEYsqlalchemy.types.TypeEngine

class sqlalchemy.dialects.mssql.SQL_VARIANT

类签名

sqlalchemy.dialects.mssql.SQL_VARIANTsqlalchemy.types.TypeEngine

class sqlalchemy.dialects.mssql.TEXT

SQL TEXT 类型。

类签名

sqlalchemy.dialects.mssql.TEXTsqlalchemy.types.Text

method __init__(length: int | None = None, collation: str | None = None)

String sqlalchemy.types.String.__init__ 方法继承

创建一个持有字符串的类型。

参数:

  • length – 可选项,用于 DDL 和 CAST 表达式中的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能要求在 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 
    

    注意

    在大多数情况下,UnicodeUnicodeText 数据类型应用于预期存储非 ASCII 数据的 Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.TIME

类签名

sqlalchemy.dialects.mssql.TIMEsqlalchemy.types.TIME

class sqlalchemy.dialects.mssql.TIMESTAMP

实现 SQL Server TIMESTAMP 类型。

注意,这与 SQL 标准的 TIMESTAMP 类型完全不同,该类型不受 SQL Server 支持。它是一个只读数据类型,不支持插入值。

版本 1.2 中的新功能。

另请参阅

ROWVERSION

成员

init()

类签名

sqlalchemy.dialects.mssql.TIMESTAMP (sqlalchemy.types._Binary)

method __init__(convert_int=False)

构造 TIMESTAMP 或 ROWVERSION 类型。

参数:

convert_int – 如果为 True,则二进制整数值将在读取时转换为整数。

新功能,版本 1.2。

class sqlalchemy.dialects.mssql.TINYINT

类签名

sqlalchemy.dialects.mssql.TINYINT (sqlalchemy.types.Integer)

class sqlalchemy.dialects.mssql.UNIQUEIDENTIFIER

成员

init()

类签名

sqlalchemy.dialects.mssql.UNIQUEIDENTIFIER (sqlalchemy.types.Uuid)

method __init__(as_uuid: bool = True)

构造一个 UNIQUEIDENTIFIER 类型。

参数:

as_uuid=True

如果为 True,则将值解释为 Python uuid 对象,通过 DBAPI 转换为/从字符串。

class sqlalchemy.dialects.mssql.VARBINARY

MSSQL VARBINARY 类型。

此类型为核心 VARBINARY 类型添加了其他功能,包括“弃用大型类型”模式,在此模式下将呈现 VARBINARY(max) 或 IMAGE,以及 SQL Server FILESTREAM 选项。

另请参阅

大型文本/二进制类型弃用

类签名

sqlalchemy.dialects.mssql.VARBINARY (sqlalchemy.types.VARBINARY, sqlalchemy.types.LargeBinary)

method __init__(length=None, filestream=False)

构造一个 VARBINARY 类型。

参数:

  • length – 可选,用于 DDL 语句中的列的长度,用于那些接受长度的二进制类型,例如 MySQL BLOB 类型。

  • filestream=False

    如果为 True,在表定义中渲染 FILESTREAM 关键字。在这种情况下,length 必须为 None'max'

    新功能,版本 1.4.31。

class sqlalchemy.dialects.mssql.VARCHAR

SQL VARCHAR 类型。

类签名

sqlalchemy.dialects.mssql.VARCHAR (sqlalchemy.types.String)

method __init__(length: int | None = None, collation: str | None = None)

String sqlalchemy.types.String.__init__ 方法继承

创建一个字符串持有类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

class sqlalchemy.dialects.mssql.XML

MSSQL XML 类型。

这是一个用于反射目的的占位符类型,不包括任何 Python 端数据类型支持。它也不支持额外的参数,如“CONTENT”、“DOCUMENT”、“xml_schema_collection”。

成员

init()

类签名

sqlalchemy.dialects.mssql.XML (sqlalchemy.types.Text)

method __init__(length: int | None = None, collation: str | None = None)

继承自 Stringsqlalchemy.types.String.__init__ 方法

创建一个持有字符串的类型。

参数:

  • length – 可选,用于 DDL 和 CAST 表达式中的列长度。如果不会发出CREATE TABLE,则可以安全地省略。某些数据库可能需要在 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 
    

    注意

    在大多数情况下,应该使用UnicodeUnicodeText数据类型来存储非 ASCII 数据的Column。这些数据类型将确保在数据库上使用正确的类型。

PyODBC

通过 PyODBC 驱动程序支持 Microsoft SQL Server 数据库。

DBAPI

PyODBC 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/pyodbc/

连接

连接字符串:

mssql+pyodbc://<username>:<password>@<dsnname>

连接到 PyODBC

此处的 URL 将被翻译为 PyODBC 连接字符串,详细信息请参阅 ConnectionStrings

DSN 连接

ODBC 中的 DSN 连接意味着在客户端机器上配置了预先存在的 ODBC 数据源。然后,应用程序指定此数据源的名称,其中包括诸如正在使用的特定 ODBC 驱动程序以及数据库的网络地址等详细信息。假设客户端已配置了数据源,则基本的基于 DSN 的连接如下所示:

engine = create_engine("mssql+pyodbc://scott:tiger@some_dsn")

以上内容将以下连接字符串传递给 PyODBC:

DSN=some_dsn;UID=scott;PWD=tiger

如果省略了用户名和密码,则 DSN 表单还将向 ODBC 字符串添加 Trusted_Connection=yes 指令。

主机名连接

主机名连接也受到了 pyodbc 的支持。这通常比 DSN 更容易使用,并且具有另一个优势,即可以在 URL 中本地指定要连接的特定数据库名称,而不是作为数据源配置的一部分固定下来。

在使用主机名连接时,驱动程序名称也必须在 URL 的查询参数中指定。由于这些名称通常带有空格,因此名称必须进行 URL 编码,这意味着使用加号代替空格:

engine = create_engine("mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=ODBC+Driver+17+for+SQL+Server")

driver 关键字对于 pyodbc 方言很重要,必须以小写形式指定。

在查询字符串中传递的任何其他名称都会在 pyodbc 连接字符串中传递,例如authenticationTrustServerCertificate等。多个关键字参数必须用与号(&)分隔;这些在生成内部 pyodbc 连接字符串时将被翻译为分号:

e = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
    "&authentication=ActiveDirectoryIntegrated"
)

可以使用 URL 构造等效的 URL:

from sqlalchemy.engine import URL
connection_url = URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssql2017",
    port=1433,
    database="test",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "TrustServerCertificate": "yes",
        "authentication": "ActiveDirectoryIntegrated",
    },
)

通过精确的 Pyodbc 字符串

一个 PyODBC 连接字符串也可以直接以 pyodbc 的格式发送,如 PyODBC 文档 中所述,使用参数 odbc_connect。一个 URL 对象可以帮助简化此过程:

from sqlalchemy.engine import URL
connection_string = "DRIVER={SQL Server Native Client 10.0};SERVER=dagger;DATABASE=test;UID=user;PWD=password"
connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})

engine = create_engine(connection_url)

使用访问令牌连接数据库

某些数据库服务器只设置为仅接受访问令牌进行登录。例如,SQL Server 允许使用 Azure Active Directory 令牌连接到数据库。这需要使用 azure-identity 库创建凭据对象。有关身份验证步骤的更多信息,请参阅 Microsoft 文档

获得引擎后,每次请求连接时都需要将凭证发送到pyodbc.connect。一种方法是在引擎上设置事件监听器,该监听器将凭证令牌添加到方言的连接调用中。更详细地讨论了这一点,可以参考生成动态认证令牌。特别是对于 SQL Server,这是作为一个 ODBC 连接属性传递的,具有由微软描述的数据结构。

以下代码片段将创建一个连接到 Azure SQL 数据库的引擎,使用 Azure 凭据连接:

import struct
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL
from azure import identity

SQL_COPT_SS_ACCESS_TOKEN = 1256  # Connection option for access tokens, as defined in msodbcsql.h
TOKEN_URL = "https://database.windows.net/"  # The token URL for any Azure SQL database

connection_string = "mssql+pyodbc://@my-server.database.windows.net/myDb?driver=ODBC+Driver+17+for+SQL+Server"

engine = create_engine(connection_string)

azure_credentials = identity.DefaultAzureCredential()

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    # remove the "Trusted_Connection" parameter that SQLAlchemy adds
    cargs[0] = cargs[0].replace(";Trusted_Connection=Yes", "")

    # create token credential
    raw_token = azure_credentials.get_token(TOKEN_URL).token.encode("utf-16-le")
    token_struct = struct.pack(f"<I{len(raw_token)}s", len(raw_token), raw_token)

    # apply it to keyword arguments
    cparams["attrs_before"] = {SQL_COPT_SS_ACCESS_TOKEN: token_struct}

提示

当没有用户名或密码时,SQLAlchemy pyodbc 方言当前会添加Trusted_Connection令牌。根据 Microsoft 的使用 Azure 访问令牌文档,这需要删除,该文档指出,使用访问令牌时的连接字符串不得包含UIDPWDAuthenticationTrusted_Connection参数。#### 避免在 Azure Synapse Analytics 上出现与事务相关的异常

Azure Synapse Analytics 在其事务处理方面与普通的 SQL Server 有显着的差异;在某些情况下,Synapse 事务内的错误可能导致服务器端任意终止,然后导致 DBAPI 的.rollback()方法(以及.commit())失败。这个问题阻止了通常的 DBAPI 合同允许.rollback()在没有事务存在时悄悄通过,因为驱动程序不期望出现这种情况。这种失败的症状是在尝试在某个操作失败后发出.rollback()时出现的异常,消息类似于“找不到相应的事务。 (111214)”。

通过以下方式向 SQL Server 方言的create_engine()函数传递ignore_no_transaction_on_rollback=True参数,可以处理此特定情况:

engine = create_engine(connection_url, ignore_no_transaction_on_rollback=True)

使用上述参数,方言将捕获在connection.rollback()期间引发的ProgrammingError异常,并在错误消息中包含代码111214时发出警告,但不会引发异常。

在版本 1.4.40 中新增了ignore_no_transaction_on_rollback=True参数。

为 Azure SQL Data Warehouse (DW) 连接启用自动提交

Azure SQL Data Warehouse 不支持事务,这可能会导致 SQLAlchemy 的“自动开始”(以及隐式提交/回滚)行为出现问题。我们可以通过在 pyodbc 和引擎级别启用自动提交来避免这些问题:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="dw.azure.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 17 for SQL Server",
        "autocommit": "True",
    },
)

engine = create_engine(connection_url).execution_options(
    isolation_level="AUTOCOMMIT"
)

避免将大型字符串参数作为 TEXT/NTEXT 发送

出于历史原因,默认情况下,Microsoft 的 SQL Server ODBC 驱动程序将长字符串参数(大于 4000 个 SBCS 字符或 2000 个 Unicode 字符)发送为 TEXT/NTEXT 值。多年来,TEXT 和 NTEXT 已经被弃用,并且开始在新版本的 SQL_Server/Azure 中引起兼容性问题。例如,请参阅此问题

从 ODBC Driver 18 for SQL Server 开始,我们可以通过使用LongAsMax=Yes连接字符串参数覆盖传统行为,并将长字符串作为 varchar(max)/nvarchar(max) 传递:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssqlserver.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "LongAsMax": "Yes",
    },
)

Pyodbc 连接池 / 连接关闭行为

PyODBC 默认使用内部连接池,这意味着连接的生命周期将比在 SQLAlchemy 中更长。由于 SQLAlchemy 有自己的连接池行为,通常最好禁用此行为。此行为只能在创建任何连接之前在 PyODBC 模块级别全局禁用,之前

import pyodbc

pyodbc.pooling = False

# don't use the engine before pooling is set to False
engine = create_engine("mssql+pyodbc://user:pass@dsn")

如果将此变量保留在其默认值True应用程序将继续保持活动数据库连接,即使 SQLAlchemy 引擎本身完全丢弃连接或引擎被处理。

另请参阅

连接池 - 在 PyODBC 文档中。

驱动程序 / Unicode 支持

PyODBC 最适合与 Microsoft ODBC 驱动程序一起使用,特别是在 Python 2 和 Python 3 上的 Unicode 支持方面。

在 Linux 或 OSX 上使用 FreeTDS ODBC 驱动程序与 PyODBC 推荐;在这个领域,包括在 Microsoft 为 Linux 和 OSX 提供 ODBC 驱动程序之前,历史上存在许多与 Unicode 相关的问题。现在 Microsoft 为所有平台提供驱动程序,对于 PyODBC 支持,建议使用这些驱动程序。FreeTDS 对于非 ODBC 驱动程序(如 pymssql)仍然很重要,在那里它运行得非常好。

行数支持

截至 SQLAlchemy 2.0.5 版本,已解决了 Pyodbc 与 SQLAlchemy ORM 的“版本化行”功能的先前限制。请参阅行数支持 / ORM 版本控制处的说明。

快速执行多个模式

PyODBC 驱动程序包括对“快速执行多个”执行模式的支持,当使用 Microsoft ODBC 驱动程序时,对于适合内存的有限大小批次的 DBAPI executemany() 调用,可以大大减少往返次数。该功能通过在要使用 executemany 调用时在 DBAPI 游标上设置属性.fast_executemany 来启用。当仅使用Microsoft ODBC 驱动程序时,SQLAlchemy PyODBC SQL Server 方言支持通过将 fast_executemany 参数传递给 create_engine() 来支持此参数:

engine = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?driver=ODBC+Driver+17+for+SQL+Server",
    fast_executemany=True)

从版本 2.0.9 开始更改:- fast_executemany参数现在对于执行多个参数集的所有 INSERT 语句具有其预期效果,不包括 RETURNING。以前,SQLAlchemy 2.0 的 insertmanyvalues 功能会导致在大多数情况下即使指定了fast_executemany也不会使用。

版本 1.3 中的新功能。

另请参阅

fast executemany - 在 github 上 ### Setinputsizes 支持

从版本 2.0 开始,pyodbc 的cursor.setinputsizes()方法用于所有语句执行,除了在fast_executemany=True时不支持cursor.executemany()调用(假设 insertmanyvalues 已启用,“fastexecutemany”在任何情况下都不会对 INSERT 语句生效)。

通过向create_engine()传递use_setinputsizes=False可以禁用cursor.setinputsizes()的使用。

use_setinputsizes保持默认值True时,传递给cursor.setinputsizes()的特定每种类型符号可以使用DialectEvents.do_setinputsizes()钩子进行程序化定制。请参阅该方法以获取用法示例。

从版本 2.0 开始更改:mssql+pyodbc 方言现在默认为所有语句执行使用use_setinputsizes=True,除了在fast_executemany=True时的 cursor.executemany()调用。可以通过向create_engine()传递use_setinputsizes=False来关闭此行为。

DBAPI

PyODBC 的文档和下载信息(如果适用)可在此处找到:pypi.org/project/pyodbc/

连接

连接字符串:

mssql+pyodbc://<username>:<password>@<dsnname>

连接到 PyODBC

此处的 URL 将被翻译为 PyODBC 连接字符串,详细信息请参阅ConnectionStrings

DSN 连接

ODBC 中的 DSN 连接意味着客户端机器上配置了预先存在的 ODBC 数据源。然后,应用程序指定此数据源的名称,其中包括诸如正在使用的特定 ODBC 驱动程序以及数据库的网络地址等详细信息。假设客户端上配置了数据源,基本的基于 DSN 的连接如下所示:

engine = create_engine("mssql+pyodbc://scott:tiger@some_dsn")

以上内容将向 PyODBC 传递以下连接字符串:

DSN=some_dsn;UID=scott;PWD=tiger

如果省略了用户名和密码,DSN 表单还将向 ODBC 字符串添加Trusted_Connection=yes指令。

主机名连接

pyodbc 也支持基于主机名的连接。这通常比使用 DSN 更容易,并且具有以下额外的优势:可以在 URL 中本地指定要连接的特定数据库名称,而不是将其作为数据源配置的固定部分。

使用主机名连接时,还必须在 URL 的查询参数中指定驱动程序名称。由于这些名称通常包含空格,因此名称必须进行 URL 编码,这意味着使用加号代替空格:

engine = create_engine("mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=ODBC+Driver+17+for+SQL+Server")

driver 关键字对于 pyodbc 方言是重要的,并且必须以小写形式指定。

在查询字符串中传递的任何其他名称都将通过 pyodbc 连接字符串传递,例如 authenticationTrustServerCertificate 等。多个关键字参数必须用和号 (&) 分隔;在内部生成 pyodbc 连接字符串时,这些将被翻译为分号:

e = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
    "&authentication=ActiveDirectoryIntegrated"
)

可以使用 URL 构造等效的 URL:

from sqlalchemy.engine import URL
connection_url = URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssql2017",
    port=1433,
    database="test",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "TrustServerCertificate": "yes",
        "authentication": "ActiveDirectoryIntegrated",
    },
)

通过准确的 Pyodbc 字符串传递

也可以直接以 pyodbc 的格式发送 PyODBC 连接字符串,如 PyODBC 文档 中所指定,使用参数 odbc_connectURL 对象可以帮助简化这一过程:

from sqlalchemy.engine import URL
connection_string = "DRIVER={SQL Server Native Client 10.0};SERVER=dagger;DATABASE=test;UID=user;PWD=password"
connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})

engine = create_engine(connection_url)

使用访问令牌连接到数据库

一些数据库服务器仅设置为仅接受访问令牌进行登录。例如,SQL Server 允许使用 Azure Active Directory 令牌连接到数据库。这需要使用 azure-identity 库创建凭据对象。有关身份验证步骤的更多信息,请参阅 Microsoft 文档

获得引擎后,每次请求连接时都需要将凭据发送给 pyodbc.connect。一种方法是在引擎上设置事件侦听器,以将凭据令牌添加到方言的连接调用中。关于这一点,可以在 生成动态认证令牌 中进行更一般的讨论。特别对于 SQL Server,这是作为 ODBC 连接属性传递的,具有由 Microsoft 描述的数据结构。

以下代码片段将创建一个使用 Azure 凭据连接到 Azure SQL 数据库的引擎:

import struct
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL
from azure import identity

SQL_COPT_SS_ACCESS_TOKEN = 1256  # Connection option for access tokens, as defined in msodbcsql.h
TOKEN_URL = "https://database.windows.net/"  # The token URL for any Azure SQL database

connection_string = "mssql+pyodbc://@my-server.database.windows.net/myDb?driver=ODBC+Driver+17+for+SQL+Server"

engine = create_engine(connection_string)

azure_credentials = identity.DefaultAzureCredential()

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    # remove the "Trusted_Connection" parameter that SQLAlchemy adds
    cargs[0] = cargs[0].replace(";Trusted_Connection=Yes", "")

    # create token credential
    raw_token = azure_credentials.get_token(TOKEN_URL).token.encode("utf-16-le")
    token_struct = struct.pack(f"<I{len(raw_token)}s", len(raw_token), raw_token)

    # apply it to keyword arguments
    cparams["attrs_before"] = {SQL_COPT_SS_ACCESS_TOKEN: token_struct}

提示

当没有用户名或密码时,SQLAlchemy pyodbc 方言当前会添加Trusted_Connection令牌。根据微软的文档,使用访问令牌时连接字符串不能包含UIDPWDAuthenticationTrusted_Connection参数,这需要删除。#### 避免 Azure Synapse Analytics 上的与事务相关的异常

Azure Synapse Analytics 在处理事务方面与普通的 SQL Server 有显着的不同;在某些情况下,Synapse 事务内的错误可能导致服务器端任意终止,这将导致 DBAPI 的.rollback()方法(以及.commit())失败。该问题阻止了通常的 DBAPI 合同,即允许.rollback()在没有事务存在时静默通过,因为驱动程序不期望出现这种情况。此失败的症状是,在某种操作失败后尝试发出.rollback()时出现类似于‘No corresponding transaction found. (111214)’的异常消息。

可以通过以下方式通过create_engine()函数将ignore_no_transaction_on_rollback=True传递给 SQL Server 方言来处理此特定情况:

engine = create_engine(connection_url, ignore_no_transaction_on_rollback=True)

使用上述参数,方言将捕获在connection.rollback()期间引发的ProgrammingError异常,并在错误消息包含代码111214时发出警告,但不会引发异常。

新版本 1.4.40 中添加了ignore_no_transaction_on_rollback=True参数。

为 Azure SQL Data Warehouse(DW)连接启用自动提交

Azure SQL Data Warehouse 不支持事务,这可能会导致 SQLAlchemy 的“autobegin”(和隐式提交/回滚)行为出现问题。我们可以通过在 pyodbc 和引擎级别启用自动提交来避免这些问题:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="dw.azure.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 17 for SQL Server",
        "autocommit": "True",
    },
)

engine = create_engine(connection_url).execution_options(
    isolation_level="AUTOCOMMIT"
)

避免将大型字符串参数发送为 TEXT/NTEXT

出于历史原因,默认情况下,Microsoft 的 SQL Server ODBC 驱动程序将长字符串参数(大于 4000 个 SBCS 字符或 2000 个 Unicode 字符)发送为 TEXT/NTEXT 值。多年来,TEXT 和 NTEXT 已经过时,并且开始与 SQL_Server/Azure 的新版本引起兼容性问题。例如,请参阅此问题

从 ODBC 驱动程序 18 开始,我们可以通过LongAsMax=Yes连接字符串参数覆盖传统行为,并将长字符串传递为 varchar(max)/nvarchar(max):

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssqlserver.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "LongAsMax": "Yes",
    },
)

DSN 连接

ODBC 中的 DSN 连接意味着客户端计算机上配置了预定义的 ODBC 数据源。然后,应用程序指定此数据源的名称,其中包括诸如正在使用的特定 ODBC 驱动程序以及数据库的网络地址等详细信息。假设客户端上配置了数据源,基本的基于 DSN 的连接如下所示:

engine = create_engine("mssql+pyodbc://scott:tiger@some_dsn")

以上内容将传递以下连接字符串给 PyODBC:

DSN=some_dsn;UID=scott;PWD=tiger

如果省略了用户名和密码,则 DSN 表单还将在 ODBC 字符串中添加 Trusted_Connection=yes 指令。

主机名连接

基于主机名的连接也受 pyodbc 支持。这些通常比 DSN 更容易使用,并且具有其他优点,即可以在 URL 中本地指定要连接的特定数据库名称,而不是作为数据源配置的一部分固定下来。

当使用主机名连接时,驱动程序名称也必须在 URL 的查询参数中指定。由于这些名称通常包含空格,因此名称必须进行 URL 编码,这意味着用加号代替空格:

engine = create_engine("mssql+pyodbc://scott:tiger@myhost:port/databasename?driver=ODBC+Driver+17+for+SQL+Server")

driver 关键字对于 pyodbc 方言至关重要,必须以小写指定。

查询字符串中传递的任何其他名称都将通过 pyodbc 连接字符串传递,例如authenticationTrustServerCertificate等。多个关键字参数必须用 ampersand (&) 分隔;在生成内部 pyodbc 连接字符串时,这些将被翻译为分号:

e = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
    "&authentication=ActiveDirectoryIntegrated"
)

可以使用 URL 构造等效的 URL:

from sqlalchemy.engine import URL
connection_url = URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssql2017",
    port=1433,
    database="test",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "TrustServerCertificate": "yes",
        "authentication": "ActiveDirectoryIntegrated",
    },
)

经过准确的 Pyodbc 字符串

PyODBC 连接字符串也可以直接以 pyodbc 格式发送,如 PyODBC 文档 中所述,使用参数 odbc_connectURL 对象可以帮助简化此过程:

from sqlalchemy.engine import URL
connection_string = "DRIVER={SQL Server Native Client 10.0};SERVER=dagger;DATABASE=test;UID=user;PWD=password"
connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string})

engine = create_engine(connection_url)

使用访问令牌连接数据库

某些数据库服务器设置为仅接受访问令牌进行登录。例如,SQL Server 允许使用 Azure Active Directory 令牌连接到数据库。这需要使用 azure-identity 库创建凭据对象。有关身份验证步骤的更多信息,请参阅 微软文档

获得引擎后,每次请求连接都需要将凭据发送到 pyodbc.connect。一种方法是在引擎上设置一个事件侦听器,该事件侦听器将凭据令牌添加到方言的连接调用中。这在 生成动态认证令牌 中更一般地讨论过。特别对于 SQL Server,这是作为 ODBC 连接属性传递的,其数据结构由 Microsoft 描述。

以下代码片段将创建一个引擎,该引擎使用 Azure 凭据连接到 Azure SQL 数据库:

import struct
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL
from azure import identity

SQL_COPT_SS_ACCESS_TOKEN = 1256  # Connection option for access tokens, as defined in msodbcsql.h
TOKEN_URL = "https://database.windows.net/"  # The token URL for any Azure SQL database

connection_string = "mssql+pyodbc://@my-server.database.windows.net/myDb?driver=ODBC+Driver+17+for+SQL+Server"

engine = create_engine(connection_string)

azure_credentials = identity.DefaultAzureCredential()

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    # remove the "Trusted_Connection" parameter that SQLAlchemy adds
    cargs[0] = cargs[0].replace(";Trusted_Connection=Yes", "")

    # create token credential
    raw_token = azure_credentials.get_token(TOKEN_URL).token.encode("utf-16-le")
    token_struct = struct.pack(f"<I{len(raw_token)}s", len(raw_token), raw_token)

    # apply it to keyword arguments
    cparams["attrs_before"] = {SQL_COPT_SS_ACCESS_TOKEN: token_struct}

提示

当没有用户名或密码时,SQLAlchemy pyodbc 方言当前会添加 Trusted_Connection 令牌。根据 Microsoft 的 Azure 访问令牌文档,当使用访问令牌时,连接字符串不得包含 UIDPWDAuthenticationTrusted_Connection 参数,因此需要删除此参数。

避免在 Azure Synapse Analytics 上出现与事务相关的异常

Azure Synapse Analytics 在事务处理方面与普通 SQL Server 有显着差异;在某些情况下,Synapse 事务中的错误可能导致服务器端任意终止,这会导致 DBAPI 的 .rollback() 方法(以及 .commit())失败。该问题阻止了通常的 DBAPI 契约,即允许 .rollback() 在没有事务存在时悄无声息地传递,因为驱动程序不期望出现这种情况。此失败的症状是在尝试在某种操作失败后发出 .rollback() 时出现类似于 'No corresponding transaction found. (111214)' 的消息的异常。

通过以下方式将 ignore_no_transaction_on_rollback=True 传递给 SQL Server 方言,可以处理这种特殊情况,通过 create_engine() 函数:

engine = create_engine(connection_url, ignore_no_transaction_on_rollback=True)

使用上述参数,方言将捕获 connection.rollback() 期间引发的 ProgrammingError 异常,并在错误消息中包含代码 111214 时发出警告,但不会引发异常。

新版本 1.4.40 中新增了 ignore_no_transaction_on_rollback=True 参数。

为 Azure SQL 数据仓库 (DW) 连接启用自动提交

Azure SQL 数据仓库不支持事务,这可能会导致 SQLAlchemy 的 “自动开始” (以及隐式提交/回滚) 行为出现问题。我们可以通过在 pyodbc 和引擎级别启用自动提交来避免这些问题:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="dw.azure.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 17 for SQL Server",
        "autocommit": "True",
    },
)

engine = create_engine(connection_url).execution_options(
    isolation_level="AUTOCOMMIT"
)

避免将大型字符串参数发送为 TEXT/NTEXT

出于历史原因,默认情况下,Microsoft 的 ODBC 驱动程序会将长字符串参数(大于 4000 个 SBCS 字符或 2000 个 Unicode 字符)发送为 TEXT/NTEXT 值。多年来,TEXT 和 NTEXT 已经被弃用,并且开始在新版本的 SQL_Server/Azure 中引起兼容性问题。例如,请参阅此问题

从 ODBC Driver 18 for SQL Server 开始,我们可以通过LongAsMax=Yes连接字符串参数覆盖传递长字符串作为 varchar(max)/nvarchar(max)的传统行为:

connection_url = sa.engine.URL.create(
    "mssql+pyodbc",
    username="scott",
    password="tiger",
    host="mssqlserver.example.com",
    database="mydb",
    query={
        "driver": "ODBC Driver 18 for SQL Server",
        "LongAsMax": "Yes",
    },
)

Pyodbc 连接池/连接关闭行为

PyODBC 默认使用内部连接池,这意味着连接的生命周期将比在 SQLAlchemy 内部更长。由于 SQLAlchemy 具有自己的连接池行为,通常最好禁用此行为。此行为只能在创建任何连接之前在 PyODBC 模块级别全局禁用:

import pyodbc

pyodbc.pooling = False

# don't use the engine before pooling is set to False
engine = create_engine("mssql+pyodbc://user:pass@dsn")

如果将此变量保留在其默认值True应用程序将继续保持活动数据库连接,即使 SQLAlchemy 引擎本身完全丢弃连接或引擎被处理。

另请参见

连接池 - 在 PyODBC 文档中。

驱动程序/Unicode 支持

PyODBC 最适合与 Microsoft ODBC 驱动程序一起使用,特别是在 Python 2 和 Python 3 的 Unicode 支持领域。

在 Linux 或 OSX 上使用 FreeTDS ODBC 驱动与 PyODBC 推荐;在这个领域历史上存在许多与 Unicode 相关的问题,包括在 Microsoft 为 Linux 和 OSX 提供 ODBC 驱动之前。现在 Microsoft 为所有平台提供驱动程序,对于 PyODBC 支持,这些是推荐的。FreeTDS 仍然适用于非 ODBC 驱动程序,如 pymssql,在这里它运行得非常好。

行数支持

截至 SQLAlchemy 2.0.5 版本,已解决了 Pyodbc 与 SQLAlchemy ORM 的“版本化行”功能的先前限制。请参阅 Rowcount Support / ORM Versioning 中的说明。

快速 Executemany 模式

PyODBC 驱动程序包括对“快速 executemany”执行模式的支持,当使用 Microsoft ODBC 驱动程序时,对于适合内存的有限大小批次的 DBAPI executemany()调用大大减少了往返次数。当要使用 executemany 调用时,在 DBAPI 游标上设置属性.fast_executemany即可启用此功能。SQLAlchemy PyODBC SQL Server 方言通过将fast_executemany参数传递给create_engine()来支持此参数,仅使用Microsoft ODBC 驱动程序

engine = create_engine(
    "mssql+pyodbc://scott:tiger@mssql2017:1433/test?driver=ODBC+Driver+17+for+SQL+Server",
    fast_executemany=True)

2.0.9 版本更改:- fast_executemany 参数现在对具有多个参数集的所有 INSERT 语句产生了预期的效果,这些参数集不包括 RETURNING。在以前的情况下,即使指定了,SQLAlchemy 2.0 的 insertmanyvalues 特性也会导致在大多数情况下不使用 fast_executemany

新功能版本 1.3。

另请参阅

快速执行多次 - 在 github 上

Setinputsizes 支持

从版本 2.0 开始,pyodbc 的 cursor.setinputsizes() 方法用于所有语句执行,除了 cursor.executemany() 调用 fast_executemany=True 的情况下不支持(假设保持 insertmanyvalues 已启用,“fastexecutemany” 将不会对任何情况下的 INSERT 语句产生作用)。

通过将 use_setinputsizes=False 传递给 create_engine() 可以禁用 cursor.setinputsizes() 的使用。

use_setinputsizes 保持默认值 True 时,传递给 cursor.setinputsizes() 的具体每种类型的符号可以使用 DialectEvents.do_setinputsizes() 钩子进行程序化定制。查看该方法以获取用法示例。

2.0 版本更改:mssql+pyodbc 方言现在默认为所有语句执行使用 use_setinputsizes=True,除了 cursor.executemany() 调用 fast_executemany=True 时的情况。可以通过将 use_setinputsizes=False 传递给 create_engine() 来关闭此行为。

pymssql

通过 pymssql 驱动程序支持 Microsoft SQL Server 数据库。

连接

连接字符串:

mssql+pymssql://<username>:<password>@<freetds_name>/?charset=utf8

pymssql 是一个提供围绕 FreeTDS 的 Python DBAPI 接口的 Python 模块。

2.0.5 版本更改:pymssql 已恢复到 SQLAlchemy 的持续集成测试中。

连接

连接字符串:

mssql+pymssql://<username>:<password>@<freetds_name>/?charset=utf8

aioodbc

通过 aioodbc 驱动程序支持 Microsoft SQL Server 数据库。

DBAPI

aioodbc 的文档和下载信息(如适用)可在此处获取:pypi.org/project/aioodbc/

连接

连接字符串:

mssql+aioodbc://<username>:<password>@<dsnname>

通过 aioodbc 驱动程序以 asyncio 风格支持 SQL Server 数据库,该驱动程序本身是围绕 pyodbc 的线程包装器。

新功能版本 2.0.23:增加了 mssql+aioodbc 方言,它是基于 pyodbc 和通用 aio* 方言架构构建的。

使用特殊的 asyncio 中介层,aioodbc 方言可作为 SQLAlchemy asyncio 扩展包的后端使用。

此驱动程序的大多数行为和注意事项与在 SQL Server 上使用的 pyodbc 方言相同;有关一般背景,请参阅 PyODBC。

此方言通常仅应与create_async_engine()引擎创建函数一起使用;否则连接样式与 pyodbc 部分中记录的相同:

from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(
    "mssql+aioodbc://scott:tiger@mssql2017:1433/test?"
    "driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes"
)

DBAPI

文档和下载信息(如果适用)可在以下网址找到:pypi.org/project/aioodbc/

连接中

连接字符串:

mssql+aioodbc://<username>:<password>@<dsnname>
posted @ 2024-06-22 11:36  绝不原创的飞龙  阅读(26)  评论(0编辑  收藏  举报