我可以在来自 Python 的 Access 数据库中同时使用本地和 ODBC 链接表吗?

Can I work with both local and ODBC linked tables in an Access database from Python?

pypyodbc 如何连接到 .accdb 数据库中的链接 table?这是否可能,或者这是 pyodbc 的限制?

我需要从 MS Acess .accdb 数据库中获取数据到 Python。这非常有效,我可以使用 pypyodbc 访问 tables 和在 .accdb 数据库中定义的查询。但是,该数据库还有 table 链接到外部 SQL 服务器。当访问这样的链接 tables pypyodbc 抱怨它无法连接到 SQL 服务器。

test.accdb 包含两个 table:Test(本地 table)和 cidb_ain(链接 SQL table )

下面的Python3代码是我尝试访问数据的:

import pypyodbc as pyodbc

cnxn = pyodbc.connect(driver='Microsoft Access Driver (*.mdb, *.accdb)',
                      dbq='test.accdb',
                      readonly=True)

cursor = cnxn.cursor()

# access to the local table works
for row in cursor.execute("select * from Test"):
    print(row)

print('----')

# access to the linked table fails
for row in cursor.execute("select * from cidb_ain"):
    print(row)

输出:

(1, 'eins', 1)
(2, 'zwei', 2)
(3, 'drei', 3)
----
Traceback (most recent call last):
  File "test_02_accdb.py", line 14, in <module>
    for row in cursor.execute("select * from cidb_ain"):
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 1605, in execute
    self.execdirect(query_string)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 1631, in execdirect
    check_success(self, ret)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 986, in check_success
    ctrl_err(SQL_HANDLE_STMT, ODBC_obj.stmt_h, ret, ODBC_obj.ansi)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 964, in ctrl_err
    raise Error(state,err_text)
pypyodbc.Error: ('HY000', "[HY000] [Microsoft][ODBC-Treiber für Microsoft Access] ODBC-Verbindung zu 'SQL Server Native Client 11.0SQLHOST' fehlgeschlagen.")

错误消息大致翻译为 "ODBC connection to 'SQL Server Native Client 11.0SQLHOST' failed"。

我无法使用 pypyodbc 通过 .accdb 数据库访问 SQL 服务器,但可以从 MS Access 中查询 cidb_ain table。此外,我可以直接连接到 SQL 服务器:

cnxn = pyodbc.connect(driver='SQL Server Native Client 11.0',
                      server='SQLHOST',
                      trusted_connection='yes',
                      database='stuffdb')

考虑到 (1) MS Access(和 Matlab 也是)可以使用 .accdb 文件中包含的信息来查询链接的 tables,以及 (2) SQL 服务器是可访问,我认为问题与 pypyodbc 有关。 (驱动程序名称和主机名在错误消息中被混淆为 'SQL Server Native Client 11.0SQLHOST' 的方式似乎也有些可疑。)

我以前没有使用 Access 的经验,所以请耐心等待,如果我遗漏了对我来说似乎不必要的重要信息,请告诉我...

首先,MS Access 是一种独特类型的数据库应用程序,与其他 RDMS(例如,SQLite、MySQL、PostgreSQL、Oracle、DB2)有些不同因为它附带默认的 back-end Jet/ACE SQL 关系引擎(顺便说一句,它不是访问受限的组件,而是通用的 Microsoft 技术)和 前端 GUI界面和报告生成器。本质上,Access是对象的集合。

链接 tables 在某种程度上是 MS Access 前端的一个功能,用于替换另一个后端的默认 Jet/ACE 数据库(即本地 tables)数据库,专门为你SQL服务器。 此外,链接的 table 本身就是 ODBC/OLEDB 连接! 您必须使用 DSN、驱动程序或提供商才能建立和创建链接的 tables 在 MS Access 文件中。

因此,连接到 MS Access 数据库 [driver='Microsoft Access Driver (*.mdb, *.accdb)] 的任何外部客户端(这里是您的 Python 脚本)实际上是连接到后端 Jet/ACE 数据库。 Client/script 从不与前端对象交互。在您的错误中 Python 读取链接 table 的 ODBC 连接,并且由于从未在脚本中调用 SQL 服务器 Driver/Provider [SQL Server Native Client 11.0SQLHOST],脚本失败.

总之,要解决您的情况,您必须 Python 直接连接到 SQL 服务器数据库(而不是使用 MS Access 作为媒介)以连接到任何本地 table那里,这里 cidb_ain。只需使用Access的连接字符串链接table:

#(USING DSN)
db = pypyodbc.connect('DSN=dsn name;')

cur = db.cursor()
cur.execute("SELECT * FROM dbo.cidb_ain")

for row in cur.fetchall()
  print(row)

cur.close()
db.close()


# (USING DRIVER)
constr = 'Trusted_Connection=yes;DRIVER={SQL Server};SERVER=servername;' \
         'DATABASE=database name;UID=username;PWD=password'
db = pypyodbc.connect(constr)

cur = db.cursor()
cur.execute("SELECT * FROM dbo.cidb_ain")

for row in cur.fetchall()
  print(row)

cur.close()
db.close()

更新:

原来解决这个问题很简单,就是在建立与Access数据库的连接之前设置pyodbc.pooling = False

import pyodbc
# ... also works with `import pypyodbc as pyodbc`, too
pyodbc.pooling = False  # this prevents the error
cnxn = pyodbc.connect(r"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ= ... ")


(上一个答案)

看来 pypyodbc 和 pyodbc 都不能从 Access 数据库中读取链接 table 的 SQL 服务器。但是,.NET 中的 System.Data.Odbc 可以做到,所以 IronPython 也可以。

为了验证,我在 SQL 服务器

中创建了一个名为 [Foods] 的 table
id  guestId  food
--  -------  ----
 1        1  pie
 2        2  soup

我在 Access 中创建了一个名为 [dbo_Foods] 的链接 table 的 ODBC,它指向 SQL 服务器上的 table。

我还创建了一个名为 [Guests] 的本地访问 table ...

id  firstName
--  ---------
 1  Gord
 2  Jenn

...和一个保存的名为 [qryGuestPreferences] 的 Access 查询 ...

SELECT Guests.firstName, dbo_Foods.food
FROM Guests INNER JOIN dbo_Foods ON Guests.id = dbo_Foods.guestId;

运行 IronPython 中的以下脚本 ...

import clr
import System
clr.AddReference("System.Data")
from System.Data.Odbc import OdbcConnection, OdbcCommand

connectString = (
    r"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};"
    r"DBQ=C:\Users\Public\Database1.accdb;"
)
conn = OdbcConnection(connectString)
conn.Open()

query = """\
SELECT firstName, food 
FROM qryGuestPreferences
"""
cmd = OdbcCommand(query, conn)
rdr = cmd.ExecuteReader()
while rdr.Read():
    print("{0} likes {1}.".format(rdr["firstName"], rdr["food"]))
conn.Close()

...returns

Gord likes pie.
Jenn likes soup.