Microsoft SQL Server

对 Microsoft SQL Server 数据库的支持。

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

支持的 Microsoft SQL Server 版本

支持类型

版本

支持版本

2012+

尽力而为

2005+

DBAPI 支持

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

外部方言

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

自动递增行为 / IDENTITY 列

SQL Server 使用所谓的“自动递增”行为 IDENTITY 结构,该结构可以放置在表中的任何单个整数列上。SQLAlchemy 在其对整数主键列的默认“自动递增”行为中考虑 IDENTITY,如 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 中更改: Identity 结构中添加了 Column 以指定 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

控制“开始”和“增量”

使用传递给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,但是部署到SQL Server数据库的基础实现类型可以使用TypeEngine.with_variant()指定为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类型的数值返回类型更改为返回浮点数,方法是将False传递给Numeric.asdecimal。为了将上面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行为

在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 中的变更: 由于SQL Server中行排序问题,SQLAlchemy版本2.0.9暂时禁用了“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 中的新增内容: 添加了SQL Server对Sequence的支持。

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

VARCHAR / NVARCHAR上的MAX

SQL Server 在 VARCHARNVARCHAR 数据类型中支持特殊字符串“MAX”,表示“可能的最大长度”。方言目前将此处理为基本类型中的长度为“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 开始通过“OFFSET n ROWS”和“FETCH NEXT n ROWS”子句添加了对 LIMIT / OFFSET 的支持。如果检测到 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_levelcreate_engine() 接受)以及 Connection.execution_options.isolation_level 参数(传递给 Connection.execution_options())来设置事务隔离级别。此功能通过为每个新连接发出命令 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 自动提交 中的讨论。

临时表/连接池资源重置

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^5HHH@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 的服务器版本,则这些类型的 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)
  • 在所有 SQLAlchemy 版本中,都可以完全控制是否渲染“旧”或“新”类型,方法是使用大写类型对象:NVARCHARVARCHARVARBINARYTEXTNTEXTIMAGE 将始终保持固定,并始终输出完全相同的类型。

多部分架构名称

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

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 的其他特殊选项。

INCLUDE

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。要禁用在每个表的基础上使用 OUTPUT INSERTED,请为每个具有触发器的 Table 指定 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 现在通过计算 RETURNING 中返回的行数来手动检索这些特定用例的“行计数”;因此,尽管驱动程序仍然存在此限制,但 ORM 版本控制功能不再受其影响。从 SQLAlchemy 2.0.5 开始,ORM 版本控制已针对 pyodbc 驱动程序完全重新启用。

在版本 2.0.5 中更改: ORM 版本控制支持已针对 pyodbc 驱动程序恢复。以前,在 ORM 刷新期间会发出有关版本控制不支持的警告。

启用快照隔离

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

ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON

有关 SQL Server 快照隔离的背景信息,请访问 https://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 服务器有效的 UPPERCASE 类型都可以从顶级方言导入,无论它们源自 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^30 个字符的可变长度 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__()

method sqlalchemy.dialects.mssql.BIT.__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 类型。

类签名

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

method sqlalchemy.dialects.mssql.CHAR.__init__(length: int | None = None, collation: str | None = None)

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

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

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

  • 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

类签名

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

class sqlalchemy.dialects.mssql.DATETIMEOFFSET

类签名

class 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 中的新增功能。

class sqlalchemy.dialects.mssql.IMAGE

成员

__init__()

method sqlalchemy.dialects.mssql.IMAGE.__init__(length: int | None = None)

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

构建 LargeBinary 类型。

参数:

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

class sqlalchemy.dialects.mssql.JSON

MSSQL JSON 类型。

MSSQL 从 SQL Server 2016 开始支持 JSON 格式的数据。

在 DDL 级别的 JSON 数据类型将表示数据类型为 NVARCHAR(max),但它还提供 JSON 级别的比较功能以及 Python 强制转换行为。

每当基础 JSON 数据类型用于 SQL Server 后端时,将自动使用 JSON 数据类型。

另请参阅

JSON - 通用跨平台 JSON 数据类型的基本文档。

通过将操作调整为在数据库级别呈现 JSON_VALUEJSON_QUERY 函数,JSON 类型支持 JSON 值的持久化以及 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__()

method sqlalchemy.dialects.mssql.JSON.__init__(none_as_null: bool = False)

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

构造一个 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
class sqlalchemy.dialects.mssql.NCHAR

SQL NCHAR 类型。

类签名

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

method sqlalchemy.dialects.mssql.NCHAR.__init__(length: int | None = None, collation: str | None = None)

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

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

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

  • 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.NTEXT

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

成员

__init__()

method sqlalchemy.dialects.mssql.NTEXT.__init__(length: int | None = None, collation: str | None = None)

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

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

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

  • 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.NVARCHAR

SQL NVARCHAR 类型。

类签名

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

method sqlalchemy.dialects.mssql.NVARCHAR.__init__(length: int | None = None, collation: str | None = None)

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

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

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

  • 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.REAL

SQL Server REAL 数据类型。

class sqlalchemy.dialects.mssql.ROWVERSION

实现 SQL Server ROWVERSION 类型。

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

ROWVERSION 数据类型从数据库本身反映(例如,内省);返回的数据类型将是 TIMESTAMP

这是一个只读数据类型,不支持 INSERT 值。

版本 1.2 中的新增内容。

另请参阅

TIMESTAMP

成员

__init__()

方法 sqlalchemy.dialects.mssql.ROWVERSION.__init__(convert_int=False)

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

构造一个 TIMESTAMP 或 ROWVERSION 类型。

参数:

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

版本 1.2 中的新增内容。

sqlalchemy.dialects.mssql.SMALLDATETIME

成员

__init__()

类签名

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

方法 sqlalchemy.dialects.mssql.SMALLDATETIME.__init__(timezone: bool = False)

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

构造一个新的 DateTime.

参数:

timezone – 布尔值。指示 datetime 类型是否应启用时区支持,如果在 **仅限基础日期/时间保持类型** 上可用。建议在使用此标志时直接使用 TIMESTAMP 数据类型,因为某些数据库包含与时区功能的 TIMESTAMP 数据类型不同的通用日期/时间保持类型,例如 Oracle。

sqlalchemy.dialects.mssql.SMALLMONEY
sqlalchemy.dialects.mssql.SQL_VARIANT
sqlalchemy.dialects.mssql.TEXT

SQL TEXT 类型。

类签名

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

方法 sqlalchemy.dialects.mssql.TEXT.__init__(length: int | None = None, collation: str | None = None)

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

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

参数:
  • length – 可选,用于 DDL 和 CAST 表达式的列长度。如果不会发出任何 CREATE TABLE,则可以安全地省略。某些数据库可能需要 length 用于 DDL,并且当发出 CREATE TABLE DDL 时,如果包含没有长度的 VARCHAR,则会引发异常。该值是解释为字节还是字符取决于数据库。

  • 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 数据类型。这些数据类型将确保在数据库上使用正确的类型。

sqlalchemy.dialects.mssql.TIME
sqlalchemy.dialects.mssql.TIMESTAMP

实现 SQL Server TIMESTAMP 类型。

请注意,这与 SQL 标准 TIMESTAMP 类型 **完全不同**,后者不受 SQL Server 支持。它是一种只读数据类型,不支持 INSERT 值。

版本 1.2 中的新增内容。

另请参阅

ROWVERSION

成员

__init__()

类签名

sqlalchemy.dialects.mssql.TIMESTAMP (sqlalchemy.types._Binary)

方法 sqlalchemy.dialects.mssql.TIMESTAMP.__init__(convert_int=False)

构造一个 TIMESTAMP 或 ROWVERSION 类型。

参数:

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

版本 1.2 中的新增内容。

sqlalchemy.dialects.mssql.TINYINT
sqlalchemy.dialects.mssql.UNIQUEIDENTIFIER

成员

__init__()

method sqlalchemy.dialects.mssql.UNIQUEIDENTIFIER.__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 选项。

类签名

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

method sqlalchemy.dialects.mssql.VARBINARY.__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 类型。

类签名

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

method sqlalchemy.dialects.mssql.VARCHAR.__init__(length: int | None = None, collation: str | None = None)

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

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

参数:
  • length – 可选,用于 DDL 和 CAST 表达式的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能需要 length 用于 DDL,并且在发出 CREATE TABLE DDL 时,如果包含没有长度的 VARCHAR,将引发异常。值是否被解释为字节或字符取决于数据库。

  • 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.XML

MSSQL XML 类型。

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

成员

__init__()

method sqlalchemy.dialects.mssql.XML.__init__(length: int | None = None, collation: str | None = None)

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

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

参数:
  • length – 可选,用于 DDL 和 CAST 表达式的列长度。如果不会发出 CREATE TABLE,则可以安全地省略。某些数据库可能需要 length 用于 DDL,并且在发出 CREATE TABLE DDL 时,如果包含没有长度的 VARCHAR,将引发异常。值是否被解释为字节或字符取决于数据库。

  • 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 数据类型。这些数据类型将确保在数据库上使用正确的类型。

PyODBC

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

DBAPI

PyODBC 的文档和下载信息(如果适用)可在以下位置获取:https://pypi.ac.cn/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 连接字符串也可以使用参数 odbc_connect 直接以 pyodbc 的格式发送,如 PyODBC 文档 中所述。使用 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 连接属性传递,并使用 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}

提示

目前,当没有用户名或密码时,Trusted_Connection 令牌由 SQLAlchemy pyodbc 方言添加。这需要根据 Microsoft 的 Azure 访问令牌文档 移除,该文档指出使用访问令牌时的连接字符串不应包含 UIDPWDAuthenticationTrusted_Connection 参数。

为 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 的 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 结合使用推荐;在历史上,该领域存在许多与 Unicode 相关的错误,包括在 Microsoft 为 Linux 和 OSX 提供 ODBC 驱动程序之前。现在 Microsoft 为所有平台提供驱动程序,对于 PyODBC 支持,建议使用这些驱动程序。FreeTDS 对于非 ODBC 驱动程序(如 pymssql)仍然具有相关性,它在该驱动程序中运行良好。

行计数支持

从 SQLAlchemy 2.0.5 开始,SQLAlchemy ORM 的“版本化行”功能与 Pyodbc 的先前限制已得到解决。请参阅 行计数支持/ORM 版本控制 中的说明。

快速 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)

Changed in version 2.0.9: - fast_executemany 参数现在具有其预期的效果,即此 PyODBC 功能对所有使用多个参数集执行且不包含 RETURNING 的 INSERT 语句生效。以前,SQLAlchemy 2.0 的 insertmanyvalues 功能会导致 fast_executemany 在大多数情况下(即使指定)也不会使用。

New in version 1.3.

另请参阅

快速 executemany - 在 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() 钩子。有关用法示例,请参阅该方法。

Changed in version 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 是一个 Python 模块,它围绕 FreeTDS 提供 Python DBAPI 接口。

Changed in version 2.0.5: pymssql 已恢复到 SQLAlchemy 的持续集成测试

aioodbc

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

DBAPI

aioodbc 的文档和下载信息(如果适用)可从以下网址获取:https://pypi.ac.cn/project/aioodbc/

连接

连接字符串

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

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

New in version 2.0.23: 添加了 mssql+aioodbc 方言,该方言构建在 pyodbc 和一般的 aio* 方言体系结构之上。

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

此驱动程序的大多数行为和注意事项与 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"
)