pymssql 官方文档翻译

英文原文档地址: http://www.pymssql.org/index.html
翻译结束于: 2020.7.24
全文严格按照原文档翻译(大概), 纯属翻译着玩, 措辞不一定严谨.
禁止商业使用

pymssql介绍

是一种 Microsoft SQL Server 的简化数据库接口, 基于 FreeTDS 构建的, 用于为 Python 提供与 SQL Server 连接的 DB-API (PEP-249) 接口.
pymssql 的 2.x 版本分支基于 FreeTDS 最新版本构建, 解决了旧的 FreeTDS 版本和 1.x 版本分支的诸多问题.

资源

特性

  • 适配 Unicode 编码
  • 适配 Python 3
  • 适用于各主流操作系统
  • 基于 Cython 编写以提高性能
  • 包含一个低等级模块 (_mssql), 帮你从复杂的 DB-API 中解放出来
  • 支持具有返回值和参数的存储过程
  • 具有综合测试套件

Works

License

Survey

Recent Changes

(以上皆是废话, 均不详表)


介绍

结构

pymssql 由以下两个模块组成:

  • pymssql – 若您在意 DB-API 的标准性或习惯了 DB-API 的语法, 可使用该模块.
  • _mssql – 如果您在意程序的性能, 并想简化代码, 您可使用它 (该模块比前者更简单)

项目社区

http://groups.google.com/group/pymssql

项目状态

当前发布: 2.x 是当前开发的分支. 它使用 Cython 和最新版本的 FreeTDS 库全面重写 (消除了旧版本 FreeTDS 的许多限制)
旧版本: 1.0.3 是旧版本, 不再开发更新

注意:
本文档适用于 pymssql 2.x .
我们假设您只使用基于 pymssql 2.x 或更新版本来编码, 所有基于旧版本的描述均被移除.
如果您需要 1.x 版本的帮助, 请移步 Google Code documentation Wiki

发展动态

(废话不表)


pymssql 示例

使用 pymssql 模块的示例脚本

基础功能 (严格的 DB-API 规范)

from os import getenv
import pymssql

server = getenv("PYMSSQL_TEST_SERVER")
user = getenv("PYMSSQL_TEST_USERNAME")
password = getenv("PYMSSQL_TEST_PASSWORD")

conn = pymssql.connect(server, user, password, "tempdb")
cursor = conn.cursor()
cursor.execute("""
IF OBJECT_ID('persons', 'U') IS NOT NULL
    DROP TABLE persons
CREATE TABLE persons (
    id INT NOT NULL,
    name VARCHAR(100),
    salesrep VARCHAR(100),
    PRIMARY KEY(id)
)
""")
cursor.executemany(
    "INSERT INTO persons VALUES (%d, %s, %s)",
    [(1, 'John Smith', 'John Doe'),
     (2, 'Jane Doe', 'Joe Dog'),
     (3, 'Mike T.', 'Sarah H.')])
# you must call commit() to persist your data if you don't set autocommit to True
# 如果你没有将参数autocommit设为true, 那么你必须调用commit()函数来提交数据
conn.commit()

cursor.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
row = cursor.fetchone()
while row:
    print("ID=%d, Name=%s" % (row[0], row[1]))
    row = cursor.fetchone()

conn.close()

迭代结果

你可以使用迭代器来代替 while 循环. 迭代器是 pymssql 对 DB-API 的扩展.

conn = pymssql.connect(server, user, password, "tempdb")
cursor = conn.cursor()
cursor.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')

for row in cursor:
    print('row = %r' % (row,))

conn.close()

行字典

在获取行 (译注:应该指的是记录) 的时候, 可以指定用字典而不是元组返回. 这样就允许使用列 (译注:应该指的是字段) 的名字访问而不是通过数字索引. 注意 as_dict 参数

conn = pymssql.connect(server, user, password, "tempdb")
cursor = conn.cursor(as_dict=True)

cursor.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
for row in cursor:
    print("ID=%d, Name=%s" % (row['id'], row['name']))

conn.close()

使用 with 语句(上下文管理器)

您可以使用 Python 的 with 语句实现连接和游标. 这样您就不必每次都去手动关闭它们了.

with pymssql.connect(server, user, password, "tempdb") as conn:
    with conn.cursor(as_dict=True) as cursor:
        cursor.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
        for row in cursor:
            print("ID=%d, Name=%s" % (row['id'], row['name']))

调用存储过程

自 pymssql 2.0.0 起, 使用 db-lib 的 rpc 接口可以实现调用存储过程

with pymssql.connect(server, user, password, "tempdb") as conn:
    with conn.cursor(as_dict=True) as cursor:
        cursor.execute("""
        CREATE PROCEDURE FindPerson
            @name VARCHAR(100)
        AS BEGIN
            SELECT * FROM persons WHERE name = @name
        END
        """)
        cursor.callproc('FindPerson', ('Jane Doe',))
        for row in cursor:
            print("ID=%d, Name=%s" % (row['id'], row['name']))

译注: 上面第 10 行中的 cursor.callproc() 方法在 API 文档中未提及, 它是用来调用指定的存储过程的方法. 因为上面第 3 行的 execute() 方法只会在数据库中建立这个存储过程, 但不会立即执行它, 必须使用 callproc() 才能运行. 该方法的第一个参数是存储过程的名字, 第二个参数是一个元组, 里面装着提供给存储过程的参数.


_mssql 示例

快速开始使用各种方法

import _mssql
conn = _mssql.connect(server='SQL01', user='user', password='password', \
    database='mydatabase')
conn.execute_non_query('CREATE TABLE persons(id INT, name VARCHAR(100))')
conn.execute_non_query("INSERT INTO persons VALUES(1, 'John Doe')")
conn.execute_non_query("INSERT INTO persons VALUES(2, 'Jane Doe')")
# how to fetch rows from a table 如何从表中获取行
conn.execute_query('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
for row in conn:
    print "ID=%d, Name=%s" % (row['id'], row['name'])
# examples of other query functions 其他查询方法的例子
numemployees = conn.execute_scalar("SELECT COUNT(*) FROM employees")
numemployees = conn.execute_scalar("SELECT COUNT(*) FROM employees WHERE name LIKE 'J%'")    # note that '%' is not a special character here
employeedata = conn.execute_row("SELECT * FROM employees WHERE id=%d", 13)
# how to fetch rows from a stored procedure 如何从存储过程中获取行
conn.execute_query('sp_spaceused')   # sp_spaceused without arguments returns 2 result sets
res1 = [ row for row in conn ]       # 1st result
res2 = [ row for row in conn ]       # 2nd result
# how to get an output parameter from a stored procedure 如何从存储过程获取输出参数
sqlcmd = """
DECLARE @res INT
EXEC usp_mystoredproc @res OUT
SELECT @res
"""
res = conn.execute_scalar(sqlcmd)
# how to get more output parameters from a stored procedure 如何从存储过程中获取更多输出参数
sqlcmd = """
DECLARE @res1 INT, @res2 TEXT, @res3 DATETIME
EXEC usp_getEmpData %d, %s, @res1 OUT, @res2 OUT, @res3 OUT
SELECT @res1, @res2, @res3
"""
res = conn.execute_row(sqlcmd, (13, 'John Doe'))
# examples of queries with parameters 带参数的查询示例
conn.execute_query('SELECT * FROM empl WHERE id=%d', 13)
conn.execute_query('SELECT * FROM empl WHERE name=%s', 'John Doe')
conn.execute_query('SELECT * FROM empl WHERE id IN (%s)', ((5, 6),))
conn.execute_query('SELECT * FROM empl WHERE name LIKE %s', 'J%')
conn.execute_query('SELECT * FROM empl WHERE name=%(name)s AND city=%(city)s', \
    { 'name': 'John Doe', 'city': 'Nowhere' } )
conn.execute_query('SELECT * FROM cust WHERE salesrep=%s AND id IN (%s)', \
    ('John Doe', (1, 2, 3)))
conn.execute_query('SELECT * FROM empl WHERE id IN (%s)', (tuple(xrange(4)),))
conn.execute_query('SELECT * FROM empl WHERE id IN (%s)', \
    (tuple([3, 5, 7, 11]),))
conn.close()

请注意迭代器的用法以及按列名访问结果的功能. 另外请注意连接方法的参数名称与 pymssql 模块中的名称不同

一个捕获异常的示例

import _mssql

try:
    conn = _mssql.connect(server='SQL01', user='user', password='password',
                          database='mydatabase')
    conn.execute_non_query('CREATE TABLE t1(id INT, name VARCHAR(50))')
except _mssql.MssqlDatabaseException as e:
    if e.number == 2714 and e.severity == 16:
        # table already existed, so quieten the error
    else:
        raise # re-raise real error
finally:
    conn.close()

文档未完成:
添加使用 _mssql 模块的一个调用存储过程的例子
译注: 是官方没写完, 不是我没翻译完


发布记录

发布记录 - 所有爆款变化和其他值得注意的事情

pymssql 2.0.0

这是 pymssql 的全新版本. 它使用 Cython 完全从头开始重写. 我们对此版本的目标是:

  • 提供对 Python 3 的支持
  • 实现对存储过程的支持
  • 为了提升性能, 使用C语言(实际上是 Cython)为 pymssql 重写 DB-API 编译器
  • 整顿清理了模块的 API 和一些代码

这就是为什么我们决定增加本版号的原因. 不幸的是, 新版本的 API 中存在旧版本与之不能兼容的变化. 有的脚本可能无法正常工作, 因此您必须审核您的代码. 如果您在意兼容性, 就暂时继续使用 pymssql 1.0.x , 然后慢慢地迁移到 2.0.

改变了项目托管方式, 现在 pymssql 托关于 GitHub:http://github.com/pymssql/pymssql

版本发行者信息:

  • Marc Abramowitz <msabramo_at_gmail_com> who joined the project in Jan 2013 and is responsible for the actual release of the 2.0 version by fixing many old tickets, coding the port to Python 3 and driving the migration to Git and GitHub.
  • Damien Churchill <damoxc_at_gmail_com> who set the foundations of the new Cython-based code base, release engineering, new site features like Sphinx, SimpleJSON and others,
  • Andrzej Kukuła <akukula_at_gmail_com> who did all the docs, site migration, and other boring but necessary stuff.
  • Jooncheol Park <jooncheol_at_gmail_com> who did develop the initial version of pymssql (until 0.5.2). Now just doing boring translation docs for Korean.
    (致敬大佬们, 但是不译)

pymssql module

  • 使用 C 重写, 相较之前性能有所提升
  • pymssql.connect()dsn 参数已移除
  • pymssql.connect()host 参数被改名为 server, 以与 _mssql 模块保持一致
  • pymssql.connect()max_conn 参数已移除

Connection 类

  • autocommit() 方法被改成 pymssql.Connection.autocommit 属性, 你可以通过它修改或获取该值

Cursor 类

_mssql 模块

旧类名 新类名
MssqlException MSSQLException
MssqlDriverException MSSQLDriverException
MssqlDatabaseException MSSQLDatabaseException
MssqlRowIterator MSSQLRowIterator
MssqlConnection MSSQLConnection

MSSQLConnection 类


FreeTDS 配置

这一节讲解与 FreeTDS 建立连接.
pymssql 使用 FreeTDS 包与 SQL Server 实例相连接. 您必须告诉它如何找到您的数据库服务器. 最基本的信息包括主机名, 端口号以及应该使用的协议版本.
操作系统级的 FreeTDS 配置文件是 /etc/freetds.conf 或者 C:\freetds.conf, 由您自己使用的操作系统决定. 也可能使用用户自定义的配置文件, 在 Linux 系统中应该是 $HOME/.freetds.conf, 而在 Windows 系统中应该是 %APPDATA%\.freetds.conf
建议的起始内容至少为:

[global]
    port = 1433
    tds version = 7.0

使用这种配置, 您就只需要给 pymssql.connect()_mssql.connect() 方法提供主机名即可

import pymssql
connection = pymssql.connect(server='mydbserver', ...)

否则您必须提供端口

connection = pymssql.connect(server='mydbserver:1433', ...)

想要连接到默认实例之外的实例, 你需要提供该实例正在监听的端口或他的实例名称

connection = pymssql.connect(server='mydbserver\\myinstancename', ...)
# or by port number (suppose you confirmed that this instance is on port 1237)
# 或通过端口号(假设您设置它监听了端口1237)
connection = pymssql.connect(server='mydbserver:1237', ...)

请参照 pymssql module reference, _mssql module reference, 以及 FAQ 的页面.
更多有关 FreeTDS 的信息请参照: http://www.freetds.org/userguide/freetdsconf.htm

测试连接

如果您确定您的数据库服务器是连通的, 但 pymssql 因为某些原因让你不能连接, 你可以使用 FreeTDS 包自带 tsql 功能检查连接

$ tsql
Usage:  tsql [-S <server> | -H <hostname> -p <port>] -U <username> [-P <password>] [-I <config file>] [-o <options>] [-t delim] [-r delim] [-D database]
(...)
$ tsql -S mydbserver -U user

注意:
以上方法适用于当且仅当您确定您的数据库连接名在 freetds.conf 里时. 否则使用 主机名/端口 格式:
$ tsql -H mydbserver -p 1433 -U user

您需要提交密码, 如果连接成功了, 您会看到 SQL 提示:

1>

接下来您就可以执行查询语句, 或者使用 exit 指令终止会话.
如果连接失败, 那么 tsql 功能会显示错误信息.


pymssql 模块参考

_mssql 模块参考

以上两节不译, 这两节全部是 API 的参考. 这一部分不知道为什么在官方文档网站上访问是 404. 想看英文原版的, 推荐直接阅读在 GitHub 上的官方文档.


从1.x 迁移至 2.x

基于严格的 DB-API 标准, 我们在保持 2.x 版本同 1.x 版本的 pymssql 接口的一致性方面付出了努力. 因此两者差距不大, 升级起来比较容易.
尽管还是有所不同...

str vs. unicode

注意, 现在讨论的是 Python 2, 因为 pymssql 1.x 在Python 3环境下不能工作.
pymssql 1.x 会返回 str 对象

>>> pymssql.__version__
'1.0.3'
>>> conn.as_dict = True
>>> cursor = conn.cursor()
>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> cursor.fetchall()
[{0: 'hello', 'str': 'hello'}]

而 pymssql 2.x 会返回 Unicode 对象

>>> pymssql.__version__
u'2.0.1.2'
>>> conn.as_dict = True
>>> cursor = conn.cursor()
>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> cursor.fetchall()
[{u'str': u'hello'}]

如果您的程序用不同的代码来处理 str 对象和 unicode 对象, 那么您可能会出现问题.
您可以通过encoding将 unicode 转换成 str

>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> s = cursor.fetchone()['str']
>>> s
u'hello'
>>> s.encode('utf-8')
'hello'

处理 uniqueidentifier 列

SQL server 有一种数据类型叫 uniqueidentifier .
在 pymssql 1.x 中, uniqueidentifier 列返回时的结果是16位的字节串. 如果您希望得到一个 uuid.UUID 对象, 您需要使用字节串自己构建.

>>> cursor.execute("SELECT * FROM foo")
>>> id_value = cursor.fetchone()['uniqueidentifier']
>>> id_value
'j!\xcf\x14D\xce\xe6B\xab\xe0\xd9\xbey\x0cMK'
>>> type(id_value)
<type 'str'>
>>> len(id_value)
16
>>> import uuid
>>> id_uuid = uuid.UUID(bytes_le=id_value)
>>> id_uuid
UUID('14cf216a-ce44-42e6-abe0-d9be790c4d4b')

在 pymssql 2.x 中, uniqueidentifier 列直接返回 uuid.UUID 对象. 如果您希望像 pymssql 1.x 那样使用字节串, 可以使用 uuid.UUID.bytes_le 来获取

>>> cursor.execute("SELECT * FROM foo")
>>> id_value = cursor.fetchone()['uniqueidentifier']
>>> id_value
UUID('14cf216a-ce44-42e6-abe0-d9be790c4d4b')
>>> type(id_value)
#<class 'uuid.UUID'>
>>> id_value.bytes_le
'j!\xcf\x14D\xce\xe6B\xab\xe0\xd9\xbey\x0cMK'

译注: 这 md 编辑器真是弱智啊, 为什么写个 <class 'uuid.UUID'> 就没代码高亮了啊. 没法子, 只好注释掉了.

pymssql.connect 的参数

参数有一点小变化, 下面是一些重要的变化.

在 pymssql 1.x 中, 提供主机信息的参数叫 host, 并且它包含了主机和端口号.
例如

conn = pymssql.connect(host='SQLHOST:1433')  # specified TCP port at a host

对于这个参数而言, 还可以使用其他语法, 用逗号代替冒号来划分主机和端口号, 指定 Windows 主机, 指定 SQL Server 实例等等.

conn = pymssql.connect(host=r'SQLHOST,5000')  # specified TCP port at a host
conn = pymssql.connect(host=r'(local)\SQLEXPRESS')  # named instance on local machine [Win]

在 pymssql 2.x 中, host 参数依旧受支持 (但我不确定它是否和 pymssql 1.x 中的拥有完全一样的功能 (译注: 这是文档作者自己说的) ). 同时还有一个叫 server 的参数用于指定主机, 与之相分离的还有一个参数叫 port.

conn = pymssql.connect(server='SQLHOST', port=1500)

参数替换

对于参数替换, pymssql 2.x 支持使用 formatpyforma. ( PEP 249 paramstyles)
注意 pyformat, PEP 249 只展示了一个字符串替换的例子, 比如

%(name)s

并不能从 PEP 249 中得知其他类型的替换是不是也支持, 比如

%(name)d
%(name)f

然而在 mailing list thread (译注: 俺也不知道这是啥) 中, 一般的共识是只有字符串可行.
请注意, pymssql 2.x 不支持 %(name)d, 而 pymssql 1.x 支持. 因此,您可能必须更改使用该格式的代码, 如下

>>> pymssql.__version__
u'2.0.1.2'
>>> pymssql.paramstyle
'pyformat'

>>> cursor.execute("select 'hello' where 1 = %(name)d", dict(name=1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pymssql.pyx", line 430, in pymssql.Cursor.execute (pymssql.c:5900)
    if not self._source._conn.nextresult():
pymssql.ProgrammingError: (102, "Incorrect syntax near '('.
DB-Lib error message 20018, severity 15:\n
General SQL Server error: Check messages from the SQL Server\n")

改成

>>> cursor.execute("select 'hello' where '1' = %(name)s", dict(name='1'))
>>> cursor.fetchall()
[(u'hello',)]

或者

>>> cursor.execute("select 'hello' where 1 = %d", 1)
>>> cursor.fetchall()
[(u'hello',)]

这个问题的例子:


常见问题

不能连接到 SQL Server

如果你的脚本不能连接到数据库, 请尝试以下步骤:

  • 检查您是否可以连接的其他工具
    如果您使用 FreeTDS , 您可以使用 tsql 指令尝试连接, 就像这样
$ tsql -H sqlserverhost -p 1433 -U user -P password -D tempdb
locale is "en_US.UTF-8"
locale charset is "UTF-8"
using default charset "UTF-8"
Setting tempdb as default database in login packet
1> SELECT @@VERSION
2> GO

Microsoft SQL Server 2012 - 11.0.2100.60 (X64)
        Feb 10 2012 19:39:15
        Copyright (c) Microsoft Corporation
        Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)

(1 row affected)

注意:
注意, 我使用了 tsql-H 参数而非 -S 参数. 因为使用前者就可以绕过读取存储着 freetds.conf 文件中关于 porttds version 的设置. 这也更像 pymssql 模块所做的事情.

如果您用 tsql 或其他工具都不能连接, 那么问题可能不是出在 pymssql 模块的身上. 您的服务器配置 (参考下文), FreeTDS 配置 或者网络等等可能出了问题.

如果您能使用 tsql 连接成功, 那您接下来可以使用 pymssql 连接数据库, 就像这样

>>> import pymssql
>>> conn = pymssql.connect(
...     server="sqlserverhost",
...     port=1433,
...     user="user",
...     password="password",
...     database="tempdb")
>>> conn
<pymssql.Connection object at 0x10107a3f8>
>>> cursor = conn.cursor()
>>> cursor.execute("SELECT @@VERSION")
>>> print(cursor.fetchone()[0])
Microsoft SQL Server 2012 - 11.0.2100.60 (X64)
  Feb 10 2012 19:39:15
  Copyright (c) Microsoft Corporation
  Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)

如果上述方法不起作用, 那么您可以尝试设置以下一两种控制日志记录的 FreeTDS 环境变量来进行诊断

  • TDSDUMP
  • TDSDUMPCONFIG
    这两者可以都安装, 也可以择其一. 他们可以被命名, 或改为 stdout / stderr.
    这会让 FreeTDS 输出一大堆反映它正在干啥的信息, 然后您就能通过这些信息发现它正在使用与您预期相悖的端口, 或其他类似的错误. 例如
>>> import os
>>> os.environ['TDSDUMP'] = 'stdout'
>>>
>>> import pymssql
>>> conn = pymssql.connect(server="sqlserverhost")
log.c:194:Starting log file for FreeTDS 0.92.dev.20140102
  on 2014-01-09 14:05:32 with debug flags 0x4fff.
config.c:731:Setting 'dump_file' to 'stdout' from $TDSDUMP.
...
dblib.c:7934:20013: "Unknown host machine name"
dblib.c:7955:"Unknown host machine name", client returns 2 (INT_CANCEL)
util.c:347:tdserror: client library returned TDS_INT_CANCEL(2)
util.c:370:tdserror: returning TDS_INT_CANCEL(2)
login.c:418:IP address pointer is empty
login.c:420:Server sqlserverhost:1433 not found!
...

注意:
您可能已经在 freetds.conf 文件中指定了端口, 但 pymssql 还是会默认使用端口 1433. 因此如果您的 SQL Server 没有运行在1433端口上, 您需要在调用 pymssql.connect 方法时特别指定参数 port. 您不能指望 freetds.conf 文件, 尽管 tsql -S 指令会这样做. 这也是为什么我会使用 tsql -H 指令来替代, 就是为了绕开这些连接问题.

tsql -C 指令也会让 FreeTDS 输出一大堆信息, 对于排查错误很有帮助

$ tsql -C
Compile-time settings (established with the "configure" script)
                            Version: freetds v0.92.dev.20140102
             freetds.conf directory: /usr/local/etc
     MS db-lib source compatibility: no
        Sybase binary compatibility: no
                      Thread safety: yes
                      iconv library: yes
                        TDS version: 5.0
                              iODBC: yes
                           unixodbc: no
              SSPI "trusted" logins: no
                           Kerberos: no
                            OpenSSL: no
                             GnuTLS: no
  • SQL Server 2005 以及更高级的版本默认不支持远程连接, 您必须使用 SQL Server Surface Area Configuration 或者 SQL Server Configuration Manager 来实现特定协议和网络配置. 设置做出改变后, 请重启 SQL Server.
  • 如果数据库在一台远程设备上, 检查连接是不是被任何防火墙, 杀毒软件或其他安全设备阻断.
  • 如果在 Linux/Unix 上使用 pymssql 模块, 检查 FreeTDS 的配置是好的, 并且 pymssql 能发现它. 最简单的办法就是使用 tsql 功能. 查看 FreeTDS Configuration 以获取更多信息.
  • 如果在 Windows 系统上使用并且数据库服务器就在本地, 您可以尝试在命令提示符中运行下面代码
REG ADD HKLM\Software\Microsoft\MSSQLServer\Client /v SharedMemoryOn /t REG_DWORD /d 1 /f

返回日期错误

如果您在Linux/*nix上使用 pymssql, 并且怀疑返回的日期不正确, 请阅读 FreeTDS和日期页面

查询没有返回记录

有一个比较有名的问题, 一些 pymssql 1.x (pymssql 1.0.2 是我发现问题的地方) 在 FreeTDS 0.82 环境下工作正常, 但更高版本的就不能正常返回记录了, 比如 FreeTDS 0.91 . 在 SurveyMonkey 上, 我们在使用 pymssql 1.0.2 并同时将 Ubuntu 10 (包含 FreeTDS 0.82) 升级到 Ubuntu 12 (包含 FreeTDS 0.91) 时遇到这个问题, 例如

>>> import pymssql
>>> pymssql.__version__
'1.0.2'
>>> conn = pymssql.connect(host='127.0.0.1:1433', user=user,
...                        password=password, database='tempdb')
>>> cursor = conn.cursor()
>>> cursor.execute('SELECT 1')
>>> cursor.fetchall()
[]

详情请看 GitHub issue 137: pymssql 1.0.2: No result rows are returned from queries with newer versions of FreeTDS.

这里有两种方法修复这个问题

  1. (推荐) 升级到 pymssql 2.x .
>>> import pymssql
>>> pymssql.__version__
u'2.0.1.2'
>>> conn = pymssql.connect(host='127.0.0.1:1433', user=user,
...                        password=password, database='tempdb')
>>> cursor = conn.cursor()
>>> cursor.execute('SELECT 1')
>>> cursor.fetchall()
[(1,)]
  1. 升级到 pymssql 1.0.3 .
>>> import pymssql
>>> pymssql.__version__
'1.0.3'
>>> conn = pymssql.connect(host='127.0.0.1:1433', user=user,
...                        password=password, database='tempdb')
>>> cursor = conn.cursor()
>>> cursor.execute('SELECT 1')
>>> cursor.fetchall()
[(1,)]

结果丢失字段

造成您的结果记录丢失字段的可能情况是您是否有配合参数 as_dict=True 来使用连接或游标, 并且您的查询中包含一个没有名称的字段
例如

>>> cursor = conn.cursor(as_dict=True)
>>> cursor.execute("SELECT MAX(x) FROM (VALUES (1), (2), (3)) AS foo(x)")
>>> cursor.fetchall()
[{}]

哇偶, MAX(x) 函数发生了什么?!?!
译注: 此处没有给输出表格的列指定字段名
在这种情况下, pymssql 不知道该给字典的 key 使用什么值, 所以它直接忽略了字段.
解决办法就是给字段加上名字

>>> cursor.execute("SELECT MAX(x) AS [MAX(x)] FROM (VALUES (1), (2), (3)) AS foo(x)")
>>> cursor.fetchall()
[{u'MAX(x)': 3}]

这个问题现在已经改了, 在 https://github.com/pymssql/pymssql/pull/160 中体现了出来. 如果你现在还像上面那样做, 就会抛出一个异常

>>> cursor.execute("SELECT MAX(x) FROM (VALUES (1), (2), (3)) AS foo(x)")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pymssql.pyx", line 426, in pymssql.Cursor.execute (pymssql.c:5828)
    raise ColumnsWithoutNamesError(columns_without_names)
pymssql.ColumnsWithoutNamesError: Specified as_dict=True and there are columns with no names: [0]

关于这个问题的例子:

pymssql 不会将 DATE 和 TIME 列反序列化为 datetime.date 和 datetime.time 实例

你或许注意到, pymssql 会将 DATETIME 对象反序列化为 datetime.datetime 对象, 但同时只会把 DATETIME 反序列化简单的字符串, 例如

>>> cursor.execute("""
... CREATE TABLE dates_and_times (
...     datetime DATETIME,
...     date DATE,
...     time TIME,
... )
... """)
>>> cursor.execute("INSERT INTO dates_and_times VALUES (GETDATE(), '20140109', '6:17')")
>>> cursor.execute("SELECT * FROM dates_and_times")
>>> cursor.fetchall()
[{u'date': u'2014-01-09', u'time': u'06:17:00.0000000',
  u'datetime': datetime.datetime(2014, 1, 9, 12, 41, 59, 403000)}]
>>> cursor.execute("DROP TABLE dates_and_times")

这个问题在于, DATETIME 已经被 FreeTDS 支持很长时间了, 但 DATE and TIME 是 SQL Server 中提供的新数据类型, 并且微软没有将对其的支持添加到 db-lib 之中, FreeTDS 也没有添加支持.
有一些关于将他们的支持添加到 FreeTDS 中的讨论, 但实际行动太磨蹭了: http://lists.ibiblio.org/pipermail/freetds/2013q2/thread.html#28348
因此我们必须等 FreeTDS 支持它, 并且大多数用户都能确保使用非常新的版本的 FreeTDS (除非在上述版本的 freetd 中有 pymssql 链接).

链接:

共享对象 “libsybdb.so.3” 没找到

在 Linux/*nix 系统中您会遇到一下这种现象

>>> import _mssql
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ImportError: Shared object "libsybdb.so.3" not found

这也许意味着 FreeTDS 不可用了, 或者它的动态链接不能找到它了. 检查它确实已经安装了, 并且保证到文件 libsybdb.so 的路径在 /etc/ld.so.conf 文件中存在. 然后使用管理员权限刷新链接数据库. 在 Solaris 系统上, 我只是在开始使用 Python 之前, 把环境变量 LD_LIBRARY_PATH 设置为该库的路径.

pymssql 2.x 已经给受支持的平台捆绑了 FreeTDS 的 sybdb 库. 这种错误只会在您使用 pymssql 2.x , 同时尝试使用自己的 FreeTDS 进行构建时出现.

DB-Lib error message 20004, severity 9: Read from SQL server failed

在 Linux/*nix 系统中可能会出现下面问题

>>> import _mssql
>>> c=_mssql.connect('hostname:portnumber','user','pass')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_mssql.DatabaseException: DB-Lib error message 20004, severity 9:
Read from SQL server failed.
DB-Lib error message 20014, severity 9:
Login incorrect.

当下面任何一个行为出现时, 都会发生这种错误

  • freetds.conf 文件找不到
  • freetds.conf 文件中的 tds version 不是 7.0 或 4.2
  • freetds.conf 文件中指定任意字符集
  • 任何不被认可的字符集被传递给了 _mssql.connect()pymssql.connect() 方法

这并不是 "Login incorrect" 这种错误, 真正的 "Login incorrect" 错误代码是 messages has code=18456 and severity=14

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342