По сути, я сравниваю 2 базы данных доступа, используя python.
У меня нет доступа, чтобы вручную открыть какие-либо файлы доступа, это должно быть сделано полностью в python!
Мне нужно получить полный список
- Имена запросов
- Связанный код запроса
Я не буду знать, какие имена запросов впереди времени.
Я пробовал несколько решений, которые почти работали, я обрисовал в общих чертах 3, закрытые ниже.
Частичное решение 1
Я почти заставил его работать, используя win32com
и метод CurrentDb.QueryDefs
для получения кода каждого запроса.
Однако, похоже, что порядок соединений не хранится детерминистически между 2 базами данных. (кажется, что это зависит от порядка записи в MSysQueries)
, т.е. в одной базе данных, текст для объединения может быть
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
После того, как первое решение не удалось, я попытался написать запрос на MSysQueries и принудительно упорядочить. Однако pyodb c не имеет доступа для чтения к таблице!
Похоже, вы не можете предоставить доступ на чтение от самого 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
.
я мог добавить модуль, но база данных доступа продолжала запрашивать имя для модуля. Я не смог найти документацию о том, как назвать модуль с помощью вызова метода
Пример кода:
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