Могу ли я работать с локальными и связанными ODBC таблицами в базе данных Access из Python?

Как pypyodbc может подключаться к связанным таблицам в базе данных .accdb? Возможно ли это вообще, или это ограничение pyodbc?

Мне нужно получить данные из базы данных MS Acess .accdb в Python. Это отлично работает, и я могу использовать pypyodbc для доступа к таблицам и запросам, определенным в базе данных .accdb. Однако в базе данных также есть таблицы, связанные с внешним SQL Server. При доступе к таким связанным таблицам pypyodbc жалуется, что не может подключиться к серверу SQL.

test.accdb содержит две таблицы: Test (локальная таблица) и cidb_ain (связанная таблица SQL)

Следующий код Python 3 - это моя попытка получить доступ к данным:

import pypyodbc as pyodbc

cnxn = pyodbc.connect(driver='Microsoft Access Driver (*.mdb, *.accdb)',
                      dbq='test.accdb',
                      readonly=True)

cursor = cnxn.cursor()

# access to the local table works
for row in cursor.execute("select * from Test"):
    print(row)

print('----')

# access to the linked table fails
for row in cursor.execute("select * from cidb_ain"):
    print(row)

Вывод:

(1, 'eins', 1)
(2, 'zwei', 2)
(3, 'drei', 3)
----
Traceback (most recent call last):
  File "test_02_accdb.py", line 14, in <module>
    for row in cursor.execute("select * from cidb_ain"):
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 1605, in execute
    self.execdirect(query_string)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 1631, in execdirect
    check_success(self, ret)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 986, in check_success
    ctrl_err(SQL_HANDLE_STMT, ODBC_obj.stmt_h, ret, ODBC_obj.ansi)
  File "C:\software\installed\miniconda3\lib\site-packages\pypyodbc.py", line 964, in ctrl_err
    raise Error(state,err_text)
pypyodbc.Error: ('HY000', "[HY000] [Microsoft][ODBC-Treiber für Microsoft Access] ODBC-Verbindung zu 'SQL Server Native Client 11.0SQLHOST' fehlgeschlagen.")

Сообщение об ошибке примерно переводится как «сбой подключения ODBC к« SQL Server Native Client 11.0SQLHOST »».

Я не могу получить доступ к SQL Server через базу данных .accdb с помощью pypyodbc, но запрос таблицы cidb_ain из MS Access возможен. Кроме того, я могу напрямую подключиться к SQL Server:

cnxn = pyodbc.connect(driver='SQL Server Native Client 11.0',
                      server='SQLHOST',
                      trusted_connection='yes',
                      database='stuffdb')

Учитывая, что (1) MS Access (и Matlab тоже) может использовать информацию, содержащуюся в файле .accdb, для запроса связанных таблиц, и (2) SQL Server доступен, я предполагаю, что проблема связана с pypyodbc. (То, как имя драйвера и имя хоста преобразовано в 'SQL Server Native Client 11.0SQLHOST' в сообщении об ошибке, тоже кажется несколько подозрительным.)

У меня нет опыта работы с Access, поэтому проявите терпение и дайте мне знать, если я пропустил важную информацию, которая показалась мне ненужной ...


person kazemakase    schedule 22.09.2015    source источник
comment
Я действительно удивлен, что все, кроме Access (здесь: Matlab), может работать со связанными таблицами через базу данных Access. Почему бы не использовать прямое подключение к SQL Server?   -  person Andre    schedule 23.09.2015
comment
База данных доступа поддерживается кем-то другим, и изменения (внешние ссылки, новые запросы, ...) часты. Я хочу избежать изменений в коде Python, например, когда добавлена ​​новая внешняя таблица.   -  person kazemakase    schedule 24.09.2015


Ответы (2)


Обновлять:

Оказывается, решение этой проблемы так же просто, как установить pyodbc.pooling = False перед установкой соединения с базой данных Access:

import pyodbc
# ... also works with `import pypyodbc as pyodbc`, too
pyodbc.pooling = False  # this prevents the error
cnxn = pyodbc.connect(r"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ= ... ")


(предыдущий ответ)

Похоже, что ни pypyodbc, ни pyodbc не могут читать связанную таблицу SQL Server из базы данных Access. Однако System.Data.Odbc в .NET может это сделать, и IronPython тоже может.

Для проверки я создал таблицу с именем [Foods] в SQL Server.

id  guestId  food
--  -------  ----
 1        1  pie
 2        2  soup

Я создал связанную таблицу ODBC с именем [dbo_Foods] в Access, которая указывает на эту таблицу на SQL Server.

Я также создал локальную таблицу доступа с именем [Гости] ...

id  firstName
--  ---------
 1  Gord
 2  Jenn

... и сохраненный запрос доступа с именем [qryGuestPreferences] ...

SELECT Guests.firstName, dbo_Foods.food
FROM Guests INNER JOIN dbo_Foods ON Guests.id = dbo_Foods.guestId;

Запуск следующего скрипта в IronPython ...

import clr
import System
clr.AddReference("System.Data")
from System.Data.Odbc import OdbcConnection, OdbcCommand

connectString = (
    r"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};"
    r"DBQ=C:\Users\Public\Database1.accdb;"
)
conn = OdbcConnection(connectString)
conn.Open()

query = """\
SELECT firstName, food 
FROM qryGuestPreferences
"""
cmd = OdbcCommand(query, conn)
rdr = cmd.ExecuteReader()
while rdr.Read():
    print("{0} likes {1}.".format(rdr["firstName"], rdr["food"]))
conn.Close()

... возвращается

Gord likes pie.
Jenn likes soup.
person Gord Thompson    schedule 26.09.2015
comment
Спасибо за ваш ответ. Это именно то, что мне нужно. Я бы предпочел использовать CPython (3.x), но на данный момент это кажется самым простым решением. - person kazemakase; 28.09.2015

Во-первых, MS Access - это уникальный тип приложения базы данных, который несколько отличается от других СУБД (например, SQLite, MySQL, PostgreSQL, Oracle, DB2), поскольку он поставляется с обоими внутренними компонентами по умолчанию Jet / ACE SQL Relational Engine (который, кстати, не является компонентом с ограниченным доступом, а является общей технологией Microsoft ), а также интерфейсный графический интерфейс и генератор отчетов. По сути, Access - это набор объектов.

Связанные таблицы - это своего рода функция внешнего интерфейса MS Access, используемая для замены базы данных Jet / ACE по умолчанию (то есть локальных таблиц) для другой серверной базы данных, специально для вашего SQL Server. Более того, связанные таблицы сами являются соединениями ODBC / OLEDB! Вы должны были использовать DSN, драйвер или поставщик даже для создания и создания связанных таблиц в файле MS Access.

Следовательно, любой внешний клиент, в данном случае ваш скрипт Python, который подключается к базе данных MS Access [driver='Microsoft Access Driver (*.mdb, *.accdb)], на самом деле подключается к базе данных Jet / ACE. Клиент / скрипт никогда не взаимодействует с объектами внешнего интерфейса. В вашей ошибке Python читает ODBC-соединение связанной таблицы, и, поскольку драйвер / поставщик SQL Server [SQL Server Native Client 11.0SQLHOST] никогда не вызывается в сценарии, сценарий завершается ошибкой.

В целом, чтобы решить вашу ситуацию, вы должны подключить Python напрямую к базе данных SQL Server (и не использовать MS Access в качестве носителя) для подключения к любым локальным таблицам там, здесь это cidb_ain. Просто используйте строку подключения связанной таблицы Access:

#(USING DSN)
db = pypyodbc.connect('DSN=dsn name;')

cur = db.cursor()
cur.execute("SELECT * FROM dbo.cidb_ain")

for row in cur.fetchall()
  print(row)

cur.close()
db.close()


# (USING DRIVER)
constr = 'Trusted_Connection=yes;DRIVER={SQL Server};SERVER=servername;' \
         'DATABASE=database name;UID=username;PWD=password'
db = pypyodbc.connect(constr)

cur = db.cursor()
cur.execute("SELECT * FROM dbo.cidb_ain")

for row in cur.fetchall()
  print(row)

cur.close()
db.close()
person Parfait    schedule 23.09.2015
comment
Спасибо за ваш ответ. Может быть, вы можете немного помочь ... В Access вы можете создавать запросы, которые хранятся в .accdb. Эти запросы из Python можно использовать так же, как если бы они были таблицами. Однако это не работает, если такой запрос содержит внешнюю ссылку (потому что, как указано выше, он не может следовать по этому соединению). Есть идеи, как разрешить эту ситуацию? - person kazemakase; 24.09.2015
comment
Опять же, Python будет использовать только таблицы в установленном соединении с базой данных. Он не может видеть ничего другого, пока не будет подключена база данных исходной таблицы. Я не уверен, что вы имеете в виду под внешней ссылкой - поле таблицы, использующее тип данных гиперссылки? Если это так, это будет импортировано в Python как строка. Если запрос использует элемент управления формы, элемент управления отчетом или функцию модуля, Python не может его использовать, поскольку не отображается интерфейсный объект, а только внутренние таблицы. - person Parfait; 24.09.2015
comment
@kazemakase - IronPython может делать то, что вы описываете. Подробнее см. мой ответ. - person Gord Thompson; 26.09.2015