使用 Pyodbc 连接到具有用户定义函数的 MS Access 文件

Posted

技术标签:

【中文标题】使用 Pyodbc 连接到具有用户定义函数的 MS Access 文件【英文标题】:Using Pyodbc to connect to an MS Access file with a User-Defined Function 【发布时间】:2021-07-02 18:43:40 【问题描述】:

我正在尝试使用 pyodbc 将 python 连接到具有用户定义函数 (UDF) 的 MS Access 数据库。

数据库中有许多查询,这利用了 UDF。我想在 python 中得到这样一个查询的结果,所以我继续使用 pyodbc。 Table1_Q2 是 Access 数据库中的查询,Arc 是 Access 数据库中的 UDF。

我使用 pyodbc 从 Access DB 中的查询中获取所有值。因此,我在 Python 中使用此 SQL 查询从 Access DB 中的查询 (Table1_Q2) 中选择所有值。我收到以下错误

Execution failed on sql 'SELECT * FROM Table1_Q2': ('42000', "[42000] [Microsoft][ODBC Microsoft Access Driver] Undefined function 'Arc' in expression. (-3102) (SQLExecDirectW)")

当我从 Access DB 运行查询时,它似乎工作正常。但是当我使用 Pyodbc 连接时,它无法识别那些利用 UDF 的查询。我可以使用 Pyodbc 访问不依赖于 UDF 的其他表。

这是一个代码sn-p:

filepath = os.path.abspath('')+'\Database1.accdb'
myDataSources = pyodbc.dataSources()
# Establishing connection to Access DB
driver = myDataSources['MS Access Database']
cnxn = pyodbc.connect(driver=driver, dbq = filepath, autocommit=True)
crsr = cnxn.cursor()
table_name = 'Table1_Q2'
query = "SELECT * FROM ".format(table_name)
source_df2 = pandas.read_sql(query, cnxn)
cnxn.close()

是否需要在代码中添加一些内容以便将 UDF 也包含在 Access DB 中。

【问题讨论】:

【参考方案1】:

您不能使用 pyodbc 和 Access ODBC 来运行涉及用户定义函数的查询。但是,如果您安装了完整的 Microsoft Access 应用程序,那么您的 Python 应用程序可以使用 COM 自动化来启动 Access 实例并以这种方式运行查询。

示例:对于包含名为 Table1 的表的 Access 数据库

id  txt
--  -------
 1  awesome

和一个包含该函数的 VBA 模块

Public Function ultra(s As String) As String
    ultra = "ultra_" & s
End Function

我们可以运行类似SELECT ultra(txt) AS u_text FROM Table1 … 的查询,如下所示:

import win32com.client  # needs `pip install pywin32`

# ACE.DAO constants
dbOpenDynaset = 2

db_path = r"C:\Users\Public\database1.accdb"
sql = "SELECT ultra(txt) AS u_text FROM Table1 WHERE id = 1"

obj_access = win32com.client.gencache.EnsureDispatch("Access.Application")
obj_access.OpenCurrentDatabase(db_path)
db = obj_access.CurrentDb()
try:
    rs = db.OpenRecordset(sql, dbOpenDynaset)
    print(rs.Fields["u_text"].Value)  # ultra_awesome
except Exception as e:
    print(e)
finally:
    rs.Close()
    obj_access.Quit()

注意事项:

    这不适用于 Microsoft Access 运行时,仅适用于完整的 Microsoft Access 产品。 这尚未通过 Office/Access 的“点击运行”安装进行测试。

【讨论】:

要使其与运行时一起工作,请使用有效数据库启动可执行文件(或使用默认可执行文件打开有效数据库),然后附加到打开的实例。 感谢@gord-thompson 的精彩解释。至于第一行提到的win32com,为什么需要它?您能否扩展您的注释以阐明取决于 Python 或 Office 安装版本的限制程度? (即,我的机器和 Python 是 64 位的,但我使用 Access 10 32 位)。我来自this problem,我想知道 COM 自动化是否也可以成为它的解决方案。另外,COM 与 ODBC 的速度相比如何?【参考方案2】:

MS Access 是multi-faceted software。虽然它是一个 GUI .exe 应用程序,其中包含许多组件,包括表单、报告、宏和模块,但它也是一个表的数据库(或数据存储),并通过 ODBC 或 OLEDB 以其他编程语言访问保存的查询。多年来,为了方便起见,“MS Access”被混为一谈,既指应用程序又指数据库。

当通过 ODBC 将 Python(或其他语言)连接到 Access 数据库时,您只能访问表和存储查询的底层数据库。事实上,您甚至不需要安装完整的 .exe 来连接,只需安装 ODBC 驱动程序(或 OLEDB 提供程序)即可。因此,无法访问保存在独立 VBA 模块中的用户定义函数。因此尝试在 SQL 语句或存储查询中使用它会引发错误。数据库中 UDF 的对应物是存储过程或函数,目前在 Access 数据库中不可用。

如前所述,另一种技术Component Object Model (COM) 公开了 MS Access 的 .exe 版本以使用其 object library,其中包括所有组件(表、查询、表单、报告、宏和模块)。事实上,VBA 和 Access 对象库是 Access .mdb 和 .accdb 项目中的两个默认外部引用!也许在未来的版本中,可以换掉 VBA 来用 Python 编写模块(即使那时 ODBC 仍然无法访问)!

【讨论】:

【参考方案3】:

Microsoft Access 数据库引擎链接到 VB,并且可以在查询中使用 VB 单词。 VB 被设计为一种可扩展语言,当它被加载时,它会挂接到父应用程序中,从而扩展语言以包含来自父应用程序的单词。

当 Microsoft Access 是父级时,它通过加载 UDF 作为对 VB 的扩展来扩展 VB,并且 UDF 可以在查询中使用。

当 Python/ODBC 是父级时,它不会通过加载 UDF 来扩展 VB。

曾经有一个用于 VBA 的开发工具包,可让您构建父 VB 应用程序,但该工具在几年前就被撤回了:它已经有 20 年没有出售了。数据库中 UDF 的存储格式是间接记录的:它是存储在数据库中的 OLE 对象。但是如果没有 Visual Basic for Applications 扩展性 API,就无法将 UDF 链接到 Python 或 ODBC。

ODBC(“开放式数据库连接”)是一种常见的通用 SQL 标准和不包含 VBA 的驱动程序接口,因此即使在 Microsoft Access 中,包含 VBA 的查询也由 Microsoft Access 首先和最后处理,而 ODBC SQL 已修复由 Microsoft Access 启动,发送到 ODBC 驱动程序,然后在数据返回给您之前再次使用任何 UDF 调用进行修复。

【讨论】:

以上是关于使用 Pyodbc 连接到具有用户定义函数的 MS Access 文件的主要内容,如果未能解决你的问题,请参考以下文章

Python pyodbc 连接到 ms 访问数据库

如果 SQL Server 是 MS 2012,则从 Pyodbc 连接到 SQL 14 Azure db

远程连接到 MS SQL - 使用 pyodbc 时出错 vs 使用 SQL Server Management Studio 成功

sqlalchemy 无法连接到 ms sql server

如何使用 pyodbc 将表从 MS Access 迁移到 Postgres?

将 sqlalchemy 连接到 MS Access