如何使用python从MS Access数据库检索所有保存的查询?
基本上,我使用python比较了2个access数据库 我没有权限手动打开任何access文件,它必须完全在python中完成 我需要检索完整的如何使用python从MS Access数据库检索所有保存的查询?,python,ms-access,pyodbc,Python,Ms Access,Pyodbc,基本上,我使用python比较了2个access数据库 我没有权限手动打开任何access文件,它必须完全在python中完成 我需要检索完整的 查询名称 关联查询代码 我不会提前知道查询的名称 我已经尝试了一些几乎奏效的解决方案,我在下面列出了3个解决方案 部分解决方案1 我几乎让它使用win32com和CurrentDb.QueryDefs方法来检索每个查询的代码 但是,连接的顺序似乎不是在两个数据库之间确定存储的。 (这似乎取决于MSysQueries中条目的顺序) i、 e.在一个
- 查询名称
- 关联查询代码
部分解决方案1 我几乎让它使用
win32com
和CurrentDb.QueryDefs
方法来检索每个查询的代码
但是,连接的顺序似乎不是在两个数据库之间确定存储的。
(这似乎取决于MSysQueries中条目的顺序)
i、 e.在一个数据库中,连接的文本可以是
on Table1.ColumnA = Table2.ColumnA & Table1.ColumnB = Table2.ColumnB
在另一个
on Table1.ColumnB = Table2.ColumnB & Table1.ColumnA = Table2.ColumnA
显然,这些将导致相同类型的联接,但不是完全相同的查询文本
如果我直接比较文本,它们不匹配。在比较之前处理文本似乎是一个坏主意,因为有很多角落案例
示例代码
objAccess = Dispatch("Access.Application")
objAccess.Visible = False
counter = 0
query_dicts = {}
for database_path in (new_database_path, old_database_path):
# Open New DB and pull stored queries into dict
objAccess.OpenCurrentDatabase(database_path)
objDB = objAccess.CurrentDb()
db_query_dict = {}
for stored_query in objDB.QueryDefs:
db_query_dict[stored_query.name] = stored_query.sql
query_dicts[("New" if counter == 0 else 'Old')] = db_query_dict
objAccess.CloseCurrentDatabase()
counter += 1
部分解决方案2 第一个解决方案失败后,我尝试在msysquerys上编写查询并强制排序。但是,pyodbc没有对该表的读取权限 看起来您无法从python本身授予读取权限,这是一个问题,在这里可能是错误的 查询:
SELECT MSysObjects.Name
, MSysQueries.Attribute
, MSysQueries.Expression
, MSysQueries.Flag
, MSysQueries.Name1
, MSysQueries.Name2
FROM MSysObjects INNER JOIN MSysQueries ON MSysObjects.Id = MSysQueries.ObjectId
order by MSysObjects.Name
, MSysQueries.Attribute
, MSysQueries.Expression
, MSysQueries.Flag
, MSysQueries.Name1
, MSysQueries.Name2
部分解决方案3
我尝试的另一件事是让python将VBA
模块存储到数据库中,该模块将元数据信息写入表中,然后通过pyodbc
读取该表
我可以添加模块,但access数据库不断提示输入模块名称。我找不到关于如何使用方法调用命名模块的文档
示例代码:
import win32com.client as win32
import comtypes, comtypes.client
import win32api, time
from win32com.client import Dispatch
strDbName = r'C:\Users\Username\SampleDatabase.mdb'
objAccess = Dispatch("Access.Application")
# objAccess.Visible = False
objAccess.OpenCurrentDatabase(strDbName)
objDB = objAccess.CurrentDb()
xlmodule = objAccess.VBE.VbProjects(1).VBComponents.Add(1) # vbext_ct_StdModule
xlmodule.CodeModule.AddFromString(Constants.ACCESS_QUERY_META_INFO_MACRO)
objAccess.Run("CreateQueryMetaInfoTable")
objAccess.CloseCurrentDatabase()
objAccess.Quit()
我试图添加的宏
Sub CreateQueryMetaInfoTable()
Dim sql_string As String
# Create empty table
CurrentDb.Execute ("Create Table QueryMetaInfoTable (QueryName text, SqlCode text)")
Dim qd As QueryDef
For Each qd In CurrentDb.QueryDefs
# insert values
sql_string = "Insert into QueryMetaInfoTable (QueryName, SqlCode) values ('" & qd.Name & "', '" & qd.SQL & "')"
CurrentDb.Execute sql_string
Next
End Sub
在@Gord Thompson的帮助下,我现在有了一个有效的解决方案 我需要先连接OLEDB以授予读取权限,生成一个包含所需信息的非系统表,然后通过ODBC将该表读回
CONNECTION_STRING_OLEDB = "PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE={};Jet OLEDB:System Database={};"
ACCESS_QUERY_META_INFO_CREATE = """SELECT MSysObjects.Name
, MSysQueries.Attribute
, MSysQueries.Expression
, MSysQueries.Flag
, MSysQueries.Name1
, MSysQueries.Name2
INTO QueryMetaInfo
FROM MSysObjects INNER JOIN MSysQueries ON MSysObjects.Id = MSysQueries.ObjectId
order by MSysObjects.Name
, MSysQueries.Attribute
, MSysQueries.Expression
, MSysQueries.Flag
, MSysQueries.Name1
, MSysQueries.Name2"""
ACCESS_QUERY_META_INFO_READ = """select * from QueryMetaInfo
order by Name
, Attribute
, Expression
, Flag
, Name1
, Name2;"""
ACCESS_QUERY_META_INFO_DROP = "DROP TABLE QueryMetaInfo"
connection = win32com.client.Dispatch(r'ADODB.Connection')
DSN = CONNECTION_STRING_OLEDB.format(database_path, r"C:\Users\C218\AppData\Roaming\Microsoft\Access\System.mdw")
connection.Open(DSN)
cmd = win32com.client.Dispatch(r'ADODB.Command')
cmd.ActiveConnection = connection
cmd.CommandText = "GRANT SELECT ON MSysObjects TO Admin;"
cmd.Execute()
connection.Execute(ACCESS_QUERY_META_INFO_CREATE)
connection.Close()
# connect with odbc to read the query meta info into pandas
connection_string = Constants.CONNECTION_STRING_ACCESS.format(database_path)
access_con = pyodbc.connect(connection_string)
access_cursor = access_con.cursor()
df = pd.read_sql(ACCESS_QUERY_META_INFO_READ, access_con)
# drop table after read
access_cursor.execute(ACCESS_QUERY_META_INFO_DROP)
access_cursor.commit
这似乎取决于MSysQueries中条目的顺序,这是胡说八道。查询文本取决于查询的创建方式,而不是其他方式。在visual builder中,这意味着将哪个列拖到哪个列。可以在任意方向创建连接。对于外部联接,根据拖动顺序,类似的操作可能导致
左联接
或右联接
。类似地,WHERE
子句的顺序取决于查询的创建方式。如果要检测导致使用不同文本执行相同的查询,则需要编写/获取SQL解析器。为了测试我的代码,我创建了一个空数据库,并从另一个数据库导入了查询。当我比较它们时,我得到了差异,因为连接的顺序已经改变了。奇怪的结果。所以我进去编辑代码来改变它。MSYS查询中没有差异。很奇怪。因此,我将代码更新为完全不同的内容,保存,然后重新更改。仍然以不同的顺序存储。听起来您正在以一种奇怪的方式导入查询。我使用的默认方法只是复制SQL。@ErikA进一步研究,结果发现在其中一个数据库中,表有一个主键,而在另一个数据库中没有。当我将主键添加到表中时,它更改了联接的存储顺序。非常奇怪的行为。“看起来您无法从python本身授予读取权限”-与其说是python,不如说是pyodbc。Microsoft为Jet/ACE数据库提供ODBC驱动程序和OLEDB提供程序。前者不能执行GRANT语句,而后者可以执行GRANT语句。