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

Posted

技术标签:

【中文标题】我可以在 Python 的 Access 数据库中同时使用本地和 ODBC 链接表吗?【英文标题】:Can I work with both local and ODBC linked tables in an Access database from Python? 【发布时间】:2015-09-22 13:44:05 【问题描述】:

pypyodbc 如何连接到 .accdb 数据库中的链接表?这是可能的,还是pyodbc的限制?

我需要将数据从 MS Acess .accdb 数据库导入 Python。这非常有效,我可以使用pypyodbc 访问在.accdb 数据库中定义的表和查询。但是,该数据库也有链接到外部 SQL Server 的表。在访问此类链接表时pypyodbc 抱怨它无法连接到 SQL 服务器。

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

以下 Python 3 代码是我访问数据的尝试:

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.")

错误消息大致翻译为“与 'SQL Server Native Client 11.0SQLHOST' 的 ODBC 连接失败”。

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

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

考虑到 (1) MS Access(和 Matlab 也是)可以使用 .accdb 文件中包含的信息来查询链接表,并且 (2) SQL Server 是可访问的,我认为问题与@987654330 有关@。 (错误消息中驱动程序名和主机名被改写为'SQL Server Native Client 11.0SQLHOST' 的方式似乎也有些可疑。)

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

【问题讨论】:

令我惊讶的是,除了 Access(此处:Matlab)之外的 任何东西 都可以通过 Access 数据库处理链接表。为什么不使用与 SQL Server 的直接连接? access 数据库由其他人维护,并且经常发生更改(外部链接、新查询等)。我想避免更改 Python 代码,例如添加了一个新的外部表。 【参考方案1】:

更新:

原来解决这个问题的方法很简单,就是在建立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 数据库中读取 SQL Server 链接表。但是, .NET 中的System.Data.Odbc 可以做到,IronPython 也可以。

为了验证,我在 SQL Server 中创建了一个名为 [Foods] 的表

id  guestId  food
--  -------  ----
 1        1  pie
 2        2  soup

我在 Access 中创建了一个名为 [dbo_Foods] 的 ODBC 链接表,它指向 SQL Server 上的那个表。

我还创建了一个名为 [Guests] 的本地 Access 表 ...

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

...和一个名为 [qryGuestPreferences] ...

的已保存访问查询
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()

...返回

Gord likes pie.
Jenn likes soup.

【讨论】:

感谢您的回答。这正是我所需要的。我更喜欢使用 CPython (3.x),但目前这似乎是最简单的解决方案。【参考方案2】:

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

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

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

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

#(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 中,您可以创建存储在 .accdb 中的查询。可以像使用表一样使用 Python 中的这些查询。但是,如果此类查询包含外部链接,则此方法不起作用(因为如上所述,它不能跟随该连接)。知道如何解决这种情况吗? 再次声明,Python 只会使用已建立的数据库连接中的表。在连接该源表的数据库之前,它无法看到任何其他内容。我不确定您所说的外部链接是什么意思 - 使用超链接数据类型的表字段?如果是这样,这将在 Python 中作为字符串导入。如果查询使用表单控件、报表控件或模块函数,则 Python 无法使用它,因为看不到前端对象,只能看到后端表。 @kazemakase - IronPython 可以按照您的描述进行操作。详情请见my answer。

以上是关于我可以在 Python 的 Access 数据库中同时使用本地和 ODBC 链接表吗?的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Python 中显示来自 Access 的数据(通过 SQL)

如何使用 Python 单击 MS Access 上的按钮

使用 Python 在 MS Access 中传输数据

使用 Python 从 MS Access 中提取数据

使用 Python 操作受用户名和密码保护的 Access 数据库

如何在python中的字符串之间添加空格。我正在从 ms access 数据库中获取数据