Basically, i'm comparing 2 access databases using python.
i do not have access to manually open any access files, it must be done entirely within python!
I need to retrieve the full list of
I will not know what the names of the queries are ahead of time.
I've tried a number of solutions that have nearly worked, i've outlined the 3 closed below.
Partial Solution 1
i nearly had it working using win32com
& the CurrentDb.QueryDefs
method to retrieve each query's code.
However, it appears that the order of the joins is not stored deterministicaly between 2 databases. (it appears to be dependent on the order of the entry in MSysQueries)
ie in one database, the text for the join could be
on Table1.ColumnA = Table2.ColumnA & Table1.ColumnB = Table2.ColumnB
and in another
on Table1.ColumnB = Table2.ColumnB & Table1.ColumnA = Table2.ColumnA
obviously these will result in the same type of join, but not the exact same query text.
If i compared the text directly they do not match. Processing the text before comparing seems like a bad idea with lots of corner cases.
Sample Code
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
Partial Solution 2
After the first solution failed, i tried to write a query on MSysQueries and force an ordering. However, pyodbc does not have read access to the table!
It appears you cannot grant read access from python itself, which is an issue, could be wrong here.
Query:
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
Partial Solution 3
Another thing i tried was to get python to store a VBA
module into the database, that will write the meta info to a table and then read that table out via pyodbc
.
i could add the module, but the access database kept prompting for a name for the module. I couldnt find the documentation on how to name the module with a method call
Sample Code:
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()
Macro i was attempting to add.
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
With the help of @Gord Thompson, i have a working solution now.
I needed to connect with OLEDB to grant the read access 1st, generated a non-system table with the info needed, then read the table back with ODBC via pandas.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.