用 mssql-python 管理连接

大多数应用程序遵循一个简单的模式:打开连接,运行查询,关闭连接。 以下各节介绍如何打开和关闭连接、使用上下文管理器、配置自动提交以及使用连接属性。

开启连接

使用该 connect() 函数建立连接。 提供一个包含服务器、数据库和身份验证详细信息的连接字符串:

import mssql_python

conn = mssql_python.connect(
    "Server=<server>.database.windows.net;Database=<database>;"
    "Authentication=ActiveDirectoryDefault;Encrypt=yes"
)

connect() 函数接受:

  • 将连接字符串作为第一个位置参数传入,或使用 connection_str 关键字。
  • 驱动程序将其合并到连接字符串中的各个关键字。
  • 其他选项如 autocommittimeoutattrs_before

你可以把这两种方法混合使用。 关键字会覆盖连接字符串中的值;当你在配置中存储基础连接字符串,并在每次调用时覆盖诸如 timeout 之类的设置时,这一特性非常有用:

# Base connection string from config, with per-call overrides
conn = mssql_python.connect(
    "Server=<server>.database.windows.net;Database=<database>;"
    "Authentication=ActiveDirectoryDefault;Encrypt=yes",
    timeout=30,
    autocommit=True
)

关闭连接

完成后一定要关闭连接,把连接还回连接池并释放服务器资源。 未封闭连接会保留服务器端内存,最终可能耗尽连接池,导致新的连接尝试被阻塞或失败。

conn = mssql_python.connect(connection_string)
try:
    # Use the connection
    cursor = conn.cursor()
    cursor.execute("SELECT 1")
finally:
    conn.close()

一旦关闭,连接将无法使用:

conn.close()
print(conn.closed)  # True

# This raises an error
cursor = conn.cursor()  # InterfaceError: Cannot create cursor on closed connection

多次调用 close() 是安全的(幂等):

conn.close()
conn.close()  # No error

上下文管理器

在大多数应用中使用该 with 语句来管理连接。 它可确保即使发生异常,驱动程序也会在代码块结束时关闭连接。 这种方法消除了因忘记 close() 通话而导致连接泄露的风险:

with mssql_python.connect(connection_string) as conn:
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE #Demo (Name NVARCHAR(50))")
    cursor.execute("INSERT INTO #Demo (Name) VALUES ('Widget')")
    conn.commit()  # Must commit explicitly when autocommit=False
# Connection automatically closed

上下文管理器在退出时关闭连接。 它 会自动提交或回滚交易:

  • 始终:退出时都会调用 close(),无论是否发生异常。
  • close() 行为:如果 autocommit=False,则在连接关闭时,所有未提交的更改都将被回滚。
  • 你必须明确调用conn.commit()以持久化更改。

该设计遵循 PEP 249 行为,防止意外部分提交。 如果你的代码在到达 commit()前触发异常,正在进行的交易将被安全回滚:

# Equivalent manual code:
conn = mssql_python.connect(connection_string)
try:
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE #Demo (Name NVARCHAR(50))")
    cursor.execute("INSERT INTO #Demo (Name) VALUES ('Widget')")
    conn.commit()  # Must commit explicitly
finally:
    conn.close()  # Rolls back uncommitted changes if autocommit=False

自动提交模式

默认情况下, autocommit=False,这意味着每个语句都运行在隐式事务中。 你必须调用 conn.commit() 以保存更改,或调用 conn.rollback() 以丢弃更改。 隐式事务是数据修改最安全的选择,因为它允许将多个语句归入一个原子操作。

当你希望每个语句立即提交时,就启用自动提交。 自动提交对于 DDL 操作(CREATE TABLEALTER INDEX)、只读工作负载或不需要事务分组的管理脚本很有用:

conn = mssql_python.connect(connection_string)
print(conn.autocommit)  # False

cursor = conn.cursor()
cursor.execute("CREATE TABLE #Demo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #Demo (Name) VALUES ('Widget')")
conn.commit()  # Required to persist changes

为每条语句启用自动提交,以便立即提交。 在连接时使用 autocommit=True,或在连接后通过 setautocommit() 或直接为属性赋值来切换它:

# At connection time
conn = mssql_python.connect(connection_string, autocommit=True)

# Or after connection (both forms work)
conn.setautocommit(True)
conn.autocommit = True
print(conn.autocommit)  # True

# Now changes are committed automatically
cursor = conn.cursor()
cursor.execute("SELECT TOP 1 Name FROM Production.Product")
print(cursor.fetchone().Name)
# No commit() needed

连接超时

设置连接超时,以控制驱动程序在引发错误之前等待建立连接的时长。 对于部署在网络不稳定环境中或服务器无法访问时快速故障的应用来说,合理的连接超时非常重要:

# At connection time (in seconds)
conn = mssql_python.connect(connection_string, timeout=30)

# Or after connection
conn.timeout = 60
print(conn.timeout)  # 60

超时时间为 0 表示无超时限制(无限期等待)。 在生产环境中设置合理的超时时间;连接尝试如果卡住且未设置超时,会永久阻塞调用线程。

连接属性

使用 set_attr() 在运行时修改连接行为。 连接属性控制低层驱动设置,如访问模式、事务隔离和数据包大小。 大多数应用不需要更改这些属性,但它们在特定场景下非常有用:

  • 只读模式:防止报告查询中的误写。
  • 事务隔离:控制并发事务的交互方式( SERIALIZABLE 用于严格一致性, READ_COMMITTED 用于通用)。
  • 数据包大小:针对高延迟或高吞吐量网络进行调优。
import mssql_python

conn = mssql_python.connect(connection_string)

# Set read-only mode
conn.set_attr(mssql_python.SQL_ATTR_ACCESS_MODE, mssql_python.SQL_MODE_READ_ONLY)

# Set transaction isolation level
conn.set_attr(mssql_python.SQL_ATTR_TXN_ISOLATION, mssql_python.SQL_TXN_SERIALIZABLE)

可用属性:

恒定 描述
SQL_ATTR_CONNECTION_TIMEOUT 连接超时(秒)。
SQL_ATTR_LOGIN_TIMEOUT 登录超时时间(秒)
SQL_ATTR_PACKET_SIZE 网络数据包大小。
SQL_ATTR_ACCESS_MODE 只读模式或读写模式。
SQL_ATTR_TXN_ISOLATION 交易隔离级别。
SQL_ATTR_CURRENT_CATALOG 当前数据库名称。

预连接属性

某些属性必须在驱动程序建立连接前设置(例如,登录超时)。 传递它们 attrs_before

conn = mssql_python.connect(
    connection_string,
    attrs_before={
        mssql_python.SQL_ATTR_LOGIN_TIMEOUT: 30,
        mssql_python.SQL_ATTR_CONNECTION_TIMEOUT: 60,
    }
)

获取连接信息

用于 getinfo() 获取驱动程序和服务器元数据,用于日志记录、诊断或根据服务器能力调整行为:

conn = mssql_python.connect(connection_string)

# Server information
print(f"Server name: {conn.getinfo(mssql_python.SQL_SERVER_NAME)}")
print(f"Database name: {conn.getinfo(mssql_python.SQL_DATABASE_NAME)}")

# Driver information
print(f"Driver name: {conn.getinfo(mssql_python.SQL_DRIVER_NAME)}")
print(f"Driver version: {conn.getinfo(mssql_python.SQL_DRIVER_VER)}")

获取可用信息常量列表:

constants = mssql_python.get_info_constants()
for name, value in constants.items():
    print(f"{name}: {value}")

搜索转义字符

searchescape 属性返回在 LIKE 模式中用于转义通配符(%_)的字符。 使用它可安全地搜索用户输入中的字面通配符字符:

escape = conn.searchescape

# Use in queries with wildcard characters
cursor.execute(
    f"SELECT Name FROM Production.Product WHERE Name LIKE '%{escape}%%' ESCAPE '{escape}'"
)
# Matches names containing literal '%' character

编码和解码

为SQL语句和结果配置文本编码。 默认设置适用于大多数应用。 只有当你连接到使用非 UTF-8 列编码 char/varchar 的服务器时,才会更改它们。 服务器使用的编码取决于 列的排序

# Set encoding for outbound text
conn.setencoding(encoding='utf-8')

# Get current encoding settings
settings = conn.getencoding()
print(settings)  # {'encoding': 'utf-8', 'ctype': ...}

# Set decoding for inbound text from specific SQL types
conn.setdecoding(mssql_python.SQL_CHAR, encoding='utf-8')

# Get current decoding settings
settings = conn.getdecoding(mssql_python.SQL_CHAR)
print(settings)

默认编码:

方向 SQL 类型 默认编码
出站(str) SQL_WCHAR utf-16le
入站 SQL_CHAR utf-8
入站 SQL_WCHAR utf-16le
入站 SQL_WMETADATA utf-16le

最佳做法

  • 对应用代码中的所有连接都使用上下文管理器with 块)。 即使有例外,他们也能保证清理。
  • 使用连接池 功能以获得更好的性能(默认启用)。 参见 连接池
  • 为你的网络环境设置合适的超时。 30 秒超时时间适用于大多数云部署;对于跨区域或 VPN 连接,应适当增大超时时间。
  • 使用 autocommit=False(默认值)来处理需要事务原子性的数据修改场景。
  • 使用 autocommit=True 执行 DDL 操作、只读查询和管理脚本。
  • 不要跨线共享联系。 驱动的线程安全等级为 1(线程可以共享模块,但不能共享连接)。