Microsoft Python SQL Server驱动 - mssql-python

mssql-python是Microsoft为SQL Server、Azure SQL 数据库、Azure SQL 托管实例和Microsoft Fabric中的SQL数据库开发的Python驱动程序。 它使用直接数据库连接(DDBC),所以你可以在不安装外部驱动管理器的情况下连接。 该驱动支持 Python 3.10 及更新版本,并符合 Python 数据库 API 规范 2.0,同时为日常开发增加了对 Python 友好的改进。

选择起点

Azure SQL的生产基线

请以此示例作为面向生产环境的 Azure SQL 连接的起点。 它从环境中读取配置,通过管理身份进行身份验证,并启用表式数据流(TDS)8.0加密。 它还会设置登录超时和每条语句的查询超时,使用指数退避机制重试瞬时故障(连接错误时使用新连接,死锁等查询错误时使用同一连接),记录结果日志,并依赖上下文管理器来释放资源。

连接字符串中的 ConnectRetryCountConnectRetryInterval 关键字可启用 SQL Server 的空闲连接复原能力:驱动程序会透明地重新连接已断开的空闲连接。 这与本示例中的应用级重试不同,后者是重试因暂时错误(如死锁或查询超时)失败的 查询 。 两者互补,所以两者都保留。

import logging
import os
import time

import mssql_python

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
logger = logging.getLogger("app")

# Transient errors that require a fresh connection to recover.
CONNECT_RETRY_ERRORS = frozenset({
    "Timeout expired",
    "Connection timeout expired",
    "Client unable to establish connection",
    "Communication link failure",
    "Connection failure during transaction",
})

# Transient errors that leave the connection usable, such as a deadlock victim
# or a query timeout, so retry on the same connection.
QUERY_RETRY_ERRORS = frozenset({
    "Serialization failure",
    "Timeout expired",
})


def connect_with_retry(conn_str: str, max_attempts: int = 3, login_timeout_s: int = 5) -> mssql_python.Connection:
    """Open a connection, retrying transient failures with exponential backoff."""
    for attempt in range(1, max_attempts + 1):
        try:
            conn = mssql_python.connect(
                conn_str,
                attrs_before={mssql_python.SQL_ATTR_LOGIN_TIMEOUT: login_timeout_s},
            )
            logger.info("connected on attempt %d/%d", attempt, max_attempts)
            return conn
        except mssql_python.OperationalError as exc:
            if exc.driver_error not in CONNECT_RETRY_ERRORS or attempt == max_attempts:
                logger.error("connect failed on attempt %d/%d: %s", attempt, max_attempts, exc.driver_error)
                raise
            delay = 2 ** (attempt - 1)  # 1s, 2s, 4s
            logger.warning(
                "connect attempt %d/%d hit transient error %r; retrying in %ds",
                attempt, max_attempts, exc.driver_error, delay,
            )
            time.sleep(delay)


def execute_with_retry(
    conn: mssql_python.Connection,
    sql: str,
    *params,
    max_attempts: int = 3,
    query_timeout_s: int = 10,
) -> mssql_python.Cursor:
    """Run sql on an open connection and return the ready-to-fetch cursor.

    Retries errors that leave the connection usable so callers don't wrap each
    query in its own function. Pass query values as parameters. Retry only
    idempotent statements; wrap writes in an explicit transaction.
    """
    for attempt in range(1, max_attempts + 1):
        cursor = mssql_python.Cursor(conn, timeout=query_timeout_s)
        try:
            cursor.execute(sql, *params)
            if attempt > 1:
                logger.info("query succeeded on attempt %d/%d", attempt, max_attempts)
            return cursor
        except mssql_python.OperationalError as exc:
            cursor.close()
            if exc.driver_error not in QUERY_RETRY_ERRORS or attempt == max_attempts:
                logger.error("query failed on attempt %d/%d: %s", attempt, max_attempts, exc.driver_error)
                raise
            delay = 2 ** (attempt - 1)  # 1s, 2s, 4s
            logger.warning(
                "query attempt %d/%d hit transient error %r; retrying in %ds",
                attempt, max_attempts, exc.driver_error, delay,
            )
            time.sleep(delay)
    raise RuntimeError("unreachable: the retry loop exits by return or raise")


def main() -> None:
    # Read configuration from the environment; never hard-code secrets.
    server = os.environ["SQL_SERVER"]      # for example, myserver.database.windows.net
    database = os.environ["SQL_DATABASE"]  # for example, AdventureWorks
    client_id = os.getenv("AZURE_CLIENT_ID")  # set for a user-assigned managed identity

    # Authenticate with the workload's managed identity over TDS 8.0 encryption.
    # ConnectRetryCount/ConnectRetryInterval transparently reconnect a dropped
    # idle connection; they don't replay a failed query.
    conn_str = (
        f"Server={server};"
        f"Database={database};"
        "Authentication=ActiveDirectoryMsi;"
        "Encrypt=strict;"
        "ConnectRetryCount=3;"
        "ConnectRetryInterval=10;"
    )
    if client_id:
        conn_str += f"UID={client_id};"

    query = """
        SELECT TOP 10
            p.BusinessEntityID,
            p.FirstName,
            p.LastName
        FROM Person.Person AS p
        ORDER BY p.BusinessEntityID;
    """

    try:
        # Context managers close the cursor and connection automatically.
        with connect_with_retry(conn_str) as conn:
            with execute_with_retry(conn, query) as cursor:
                for business_entity_id, first_name, last_name in cursor.fetchall():
                    print(f"{business_entity_id}\t{first_name}\t{last_name}")
    except mssql_python.Error:
        logger.exception("query failed")
        raise


if __name__ == "__main__":
    main()

关于本示例中每个问题的更深入指导,请参见 Microsoft Entra 认证连接池加密与证书重试逻辑错误处理

主要功能

  • PEP 249 合规性:标准 connectcursorexecutefetch* 接口,以及 Python 扩展。
  • 直接数据库连接(DDBC):无需外部驱动程序管理器。 安装完 mssql-python 毕,你就可以连接了。
  • Microsoft Entra ID 身份验证:内置对身份验证模式的支持,包括托管标识和服务主体。
  • SQL Server 和 Windows 身份验证:支持平台上的 SQL 登录、Kerberos 和 Windows 单点登录(SSO)。
  • 批量复制:支持原生 TDS 协议的高性能批量插入,适用于大规模数据加载。
  • 原生数据类型支持JSON、XML、空间、稀疏列、datetimeoffset、十进制/货币 ,并能精确处理。
  • Apache Arrow 集成零拷贝结果集,可与 pandas、Polars 和 DuckDB 进行快速数据交换。
  • 异步模式:通过 ThreadPoolExecutor 变通方案,可将该驱动程序用于基于 asyncio 的应用程序和 FastAPI。 关于积分模式,请参见 异步模式
  • 默认支持TLS:默认启用 TLS加密和证书验证 (通过ODBC驱动18)。 当你设置 Encrypt=strict时,可以使用 TDS 8.0 加密。

开始

文章 描述
安装 安装mssql-python并验证你的Python环境。
快速入门:连接mssql-python 连接到本地或测试的 SQL Server 实例,运行你的第一个查询。
快速入门:从Jupyter Notebook连接 在笔记本中使用 mssql-python 进行交互式数据探索。
快速入门:批量复制 将大型数据集通过批量复制API迁移到SQL Server。
快速入门:快速原型制作 快速构建小型脚本和概念验证。
快速启动:可重复部署 打包、配置并发布与SQL通信的Python应用程序。
Apache Arrow 快速入门 将查询结果导出为Apache Arrow表格,用于分析工作流。

配置和认证

文章 描述
连接字符串 连接字符串语法、常用关键词和示例。
程序化构建连接字符串 根据配置和机密安全地构建连接字符串。
连接管理 干净利落地打开、重复使用和关闭连接。
连接池 泳池调校、寿命和重复利用模式。
加密与证书 TLS加密模式、证书验证和TDS 8.0。
Microsoft Entra 身份验证 无密码认证适用于Azure SQL,包含管理身份、服务主体、交互式和设备代码流程。
安全最佳做法 参数化、机密管理、最低特权和加密。
可用性组 连接到 Always On 可用性组和只读副本。

处理数据

文章 描述
执行查询 executeexecutemany、多语句批处理和结果集。
获取数据 fetchonefetchmanyfetchall和流式模式。
参数化查询 安全绑定参数以防止SQL注入。
存储过程 调用过程,读取输出参数,处理结果集。
光标管理 光标寿命、滚动和数组大小调优。
行对象 通过索引、名称或映射形式访问行。
事务管理 提交、回滚、保存点和隔离级别。
分页 针对大型结果集的键集分页和偏移分页模式。
错误处理 mssql_python.ErrorDatabaseError以及 SQL Server 错误结构。
重试逻辑 检测瞬态错误,并用指数回馈重试。

SQL Server 数据类型与特性

文章 描述
数据类型映射 SQL Server 到 Python 类型的表格和转换规则。
日期时间处理 datetimedatetime2datetimeoffset以及时区注意事项。
十进制和货币类型 精确数值类型和 decimal.Decimal 精度。
字符串和Unicode数据 varcharnvarchar、排序和代码页。
NULL 处理 三值逻辑、哨兵和熊猫互操作。
二进制数据 varbinaryimage以及大型对象流式传输。
定制类型转换器 为自定义类型注册输入/输出转换器。
批量复制操作 使用批量复制 API 进行高吞吐量插入。
JSON 数据 使用 FOR JSONOPENJSON 存储、查询和分解 JSON。
XML数据 使用 xml 数据类型、XPath 和 XQuery。
空间数据 geometry以及geography来自 Python 的类型。
稀疏列 宽表使用稀疏列和列集。
架构发现 检查数据库、表格、列和索引。

与 Python 工具和框架集成

文章 描述
Apache Arrow 集成 将结果作为箭头表获取,用于零拷贝分析。
Pandas整合 将查询结果加载到DataFrame中并写回。
极星积分 用 Polars 配合 mssql-python 处理列式工作负载。
DuckDB 集成 同时查询 SQL Server 数据和本地 DuckDB 表。
FastAPI 集成 把 mssql-python 接入 FastAPI 服务。
Flask 集成 在Flask应用中使用mssql-python。
异步模式 将mssql-python与 asyncio 线程池结合起来。
数据访问与分析模式 为通过游标访问数据、Arrow 提取数据、pandas、Polars 和 DuckDB 对 SQL 数据进行分析选择合适的读取路径。
数据加载与移动模式 选择正确的写入路径,用于逐行插入、批量复制、MERGE upsert 操作、DataFrame 加载和导入 CSV 数据。

部署和操作

文章 描述
容器和本地开发 为连接SQL的Python应用设置Docker容器、开发容器和CI流水线。
性能优化 连接池调优、预处理语句、批处理大小和批量复制。
Troubleshooting 常见错误、日志记录和证书诊断。
模块配置 模块级设置、日志钩子和功能标志。

迁移到 mssql-python

文章 描述
从 pyodbc 迁移 将 pyodbc API 和连接字符串映射到 mssql-python。
从 pymssql 迁移 用mssql-python替换pymssql,同时保留行为。
从 SQLite 迁移 将本地SQLite工作负载迁移到SQL Server或Azure SQL。
从 PostgreSQL 迁移 Python 开发者使用 mssql-python 从 PostgreSQL 迁移到 SQL Server 的一站式指南

Reference

文章 描述
支持生命周期 受支持的 Python 和 SQL Server 版本及更新频率
新增功能 版本历史和发布亮点。