英文原文档地址: 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 版本分支的诸多问题.
资源
-
Docs & Project Home
- Quick Start: coming soon :)
- FAQ & Troubleshooting
- PYPI Project
- GitHub
- Discussion
- FreeTDS User Guide
特性
- 适配 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 类
-
fetchone_asdict()
方法被移除. 与参数as_dict=True
一起使用pymssql.connect()
, 然后使用常规的fetchone()
-
fetchmany_asdict()
方法被移除. 与参数as_dict=True
一起使用pymssql.connect()
, 然后使用常规的fetchmany()
-
fetchall_asdict()
方法被移除. 与参数as_dict=True
一起使用pymssql.connect()
, 然后使用常规的fetchall()
_mssql 模块
- 添加对存储过程 (
MSSQLStoredProcedure
class)的本地支持 -
_mssql.connect()
方法的maxconn
参数已移除 -
_mssql.connect()
方法的timeout
和login_timeout
参数已添加 - 模块添加新方法
get_max_connections()
和set_max_connections()
- 类名发生变换的新旧对比表如下
旧类名 | 新类名 |
---|---|
MssqlException | MSSQLException |
MssqlDriverException | MSSQLDriverException |
MssqlDatabaseException | MSSQLDatabaseException |
MssqlRowIterator | MSSQLRowIterator |
MssqlConnection | MSSQLConnection |
MSSQLConnection 类
- 添加
tds_version
属性
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 上的官方文档.
-
Github:
_mssql
文档
至于为什么不译, 因为已经有大佬在CSDN上发布了翻译, 所以不再重复发明轮子.
_mssql 模块官方文档的翻译
(侵立删)
从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 支持使用 format
和 pyforma
. ( 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',)]
这个问题的例子:
- Google Group post: paramstyle changed?
- GitHub issue #155: pymssql 2.x does not support “%(foo)d” parameter substitution style; pymssql 1.x did
常见问题
不能连接到 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
文件中关于port
和tds 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()
[]
这里有两种方法修复这个问题
- (推荐) 升级到 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,)]
- 升级到 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
对象, 但同时只会把 DATE
和 TIME
反序列化简单的字符串, 例如
>>> 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 链接).
链接:
- https://github.com/pymssql/pymssql/issues/156
- Discussion of adding support for DATE and TIME to FreeTDS
共享对象 “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