Как получить все сохраненные запросы из базы данных MS Access, используя python? - PullRequest
0 голосов
/ 08 апреля 2020

По сути, я сравниваю 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

1 Ответ

1 голос
/ 09 апреля 2020

С помощью @Gord Thompson у меня теперь есть рабочее решение.

Мне нужно было соединиться с OLEDB, чтобы сначала предоставить доступ для чтения, сгенерировал несистемную таблицу с необходимой информацией, затем прочитал стол обратно с ODB C через 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...