То, что казалось тривиальной задачей, превратилось в настоящий кошмар, когда мне пришлось читать некоторые данные из модели PowerPivot с использованием Python. Я полагаю, что я очень хорошо исследовал это за последние пару дней, но теперь я попал в кирпичную стену и был бы признателен за помощь сообщества Python/SSAS/ADO.
По сути, все, что я хочу сделать, - это программный доступ к необработанным данным, хранящимся в моделях PowerPivot. Моя идея заключалась в том, чтобы подключиться к базовому ядру PowerPivot (т.е. MS Analysis Services) с помощью одного из методов, перечисленных ниже, перечислить таблицы, содержащиеся в модели, затем извлечь необработанные данные из каждой таблицы, используя простой DAX-запрос (что-то вроде EVALUATE (table_name)
). Легко peasy, верно? Ну, может и нет.
0. Некоторая справочная информация
Как видите, я пробовал несколько разных подходов. Я постараюсь документировать все как можно более тщательно, чтобы у тех, кто не связан с функциональностью PowerPivot, было хорошее представление о том, что я хотел бы сделать.
Прежде всего, некоторые сведения о программном доступе к ядру служб Analysis Services (в нем говорится, что SQL Server 2005, но все это должно быть применимо): возможности программирования SQL Server Data Mining и поставщики данных, используемые для подключений служб Analysis Services.
Пример файла Excel/PowerPivot, который я буду использовать в приведенном ниже примере, можно найти здесь: Примеры Microsoft PowerPivot для Excel 2010 и PowerPivot в Excel 2013.
Кроме того, обратите внимание, что я использую Excel 2010, поэтому часть моего кода зависит от версии. Например, wb.Connections["PowerPivot Data"].OLEDBConnection.ADOConnection
должно быть wb.Model.DataModelConnection.ModelConnection.ADOConnection
если вы используете Excel 2013.
Строка подключения, которую я буду использовать в этом вопросе, основана на информации, найденной здесь: Подключитесь к движку PowerPivot с помощью С#. Кроме того, некоторые методы, по-видимому, требуют некоторой инициализации модели PowerPivot перед извлечением данных. Смотрите здесь: Автоматизация операции обновления PowerPivot из VBA.
Наконец, вот пара ссылок, показывающих, что это должно быть достижимо (заметьте, однако, что эти ссылки в основном относятся к С#, а не к Python):
- Установлено соединение с PowerPivot DataModel, как я могу заполнить им набор данных?
- Подключение к PowerPivot с помощью С#
- 2013 С# подключение к PowerPivot DataModel
- Подключение Tableau и PowerPivot. Это просто работает. (показывая, что внешние приложения могут фактически считывать данные модели PowerPivot - обратите внимание, что надстройка Tableau устанавливает сборку
Interop.ADODB.dll
, которая, как мне кажется, используется для доступа к данным PowerPivot)
1. Использование ADOMD
import clr
clr.AddReference("Microsoft.AnalysisServices.AdomdClient")
import Microsoft.AnalysisServices.AdomdClient as ADOMD
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = ADOMD.AdomdConnection(ConnString)
Connection.Open()
Здесь, похоже, проблема в том, что модель PowerPivot не была инициализирована:
AdomdConnectionException: A connection cannot be made. Ensure that the server is running.
2. Использование AMO
import clr
clr.AddReference("Microsoft.AnalysisServices")
import Microsoft.AnalysisServices as AMO
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = AMO.Server()
Connection.Connect(ConnString)
Та же история, "сервер не работает":
ConnectionException: A connection cannot be made. Ensure that the server is running.
Обратите внимание, что AMO технически не используется для запроса данных, но я включил его в качестве одного из потенциальных способов подключения к модели PowerPivot.
3. Использование ADO.NET
import clr
clr.AddReference("System.Data")
import System.Data.OleDb as ADONET
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = ADONET.OleDbConnection()
Connection.ConnectionString = ConnString
Connection.Open()
Это похоже на Какой самый простой способ получить доступ к mssql с помощью python или ironpython? , К сожалению, это также не работает:
OleDbException: OLE DB error: OLE DB or ODBC error: The following system error occurred:
The requested name is valid, but no data of the requested type was found.
4. Использование ADO через модуль adodbapi
import adodbapi
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = adodbapi.connect(ConnString)
Аналогично противоположной работе OLEDB/ODBC между Python и MS Access VBA. Я получаю ошибку:
OperationalError: (com_error(-2147352567, 'Exception occurred.', (0, u'Microsoft OLE DB
Provider for SQL Server 2012 Analysis Services.', u'OLE DB error: OLE DB or ODBC error: The
following system error occurred: The requested name is valid, but no data of the requested
type was found...
Это в основном та же проблема, что и в ADO.NET выше.
5. Использование ADO через модуль Excel/win32com
from win32com.client import Dispatch
Xlfile = "H:\\PowerPivotTutorialSample.xlsx"
XlApp = Dispatch("Excel.Application")
Workbook = XlApp.Workbooks.Open(Xlfile)
Workbook.Connections["PowerPivot Data"].Refresh()
Connection = Workbook.Connections["PowerPivot Data"].OLEDBConnection.ADOConnection
Recordset = Dispatch('ADODB.Recordset')
Query = "EVALUATE(dbo_DimDate)" #sample DAX query
Recordset.Open(Query, Connection)
Идея такого подхода пришла из этого поста в блоге, в котором используется VBA: экспорт таблицы или DAX-запроса из Power Pivot в CSV с использованием VBA. Обратите внимание, что в этом подходе используется явная команда Refresh, которая инициализирует модель (т.е. "Сервер"). Вот сообщение об ошибке:
com_error: (-2147352567, 'Exception occurred.', (0, u'ADODB.Recordset', u'Arguments are of
the wrong type, are out of acceptable range, or are in conflict with one another.',
u'C:\\Windows\\HELP\\ADO270.CHM', 1240641, -2146825287), None)
Похоже, однако, что соединение ADO установлено:
-
type(Connection)
возвращаетinstance
-
print(Connection)
возвращаетProvider=MSOLAP.5;Persist Security Info=True;Initial Catalog=Microsoft_SQLServer_AnalysisServices;Data Source=$Embedded$;MDX Compatibility=1;Safety Options=2;ConnectTo=11.0;MDX Missing Member Mode=Error;Subqueries=2;Optimize Response=3;Cell Error Mode=TextValue
Кажется, проблема заключается в создании объекта ADODB.Recordset.
6. Использование ADO через Excel/win32com, прямое использование ADODB.Connection
from win32com.client import Dispatch
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = Dispatch('ADODB.Connection')
Connection.Open(ConnString)
Аналогично подключению к доступу из Python [duplicate] и Query access с использованием ADO на платформе Win32 (рецепт Python). К сожалению, ошибка, которую выдает Python, такая же, как в двух приведенных выше примерах:
com_error: (-2147352567, 'Exception occurred.', (0, u'Microsoft OLE DB Provider for SQL
Server 2012 Analysis Services.', u'OLE DB error: OLE DB or ODBC error: The following system
error occurred: The requested name is valid, but no data of the requested type was found.
..', None, 0, -2147467259), None)
7. Использование ADO через Excel/win32com, прямое использование ADODB.Connection плюс обновление модели
from win32com.client import Dispatch
Xlfile = "H:\\PowerPivotTutorialSample.xlsx"
XlApp = Dispatch("Excel.Application")
Workbook = XlApp.Workbooks.Open(Xlfile)
Workbook.Connections["PowerPivot Data"].Refresh()
ConnStringInternal = "Provider=MSOLAP.5;Persist Security Info=True;Initial Catalog=
Microsoft_SQLServer_AnalysisServices;Data Source=$Embedded$;MDX
Compatibility=1;Safety Options=2;ConnectTo=11.0;MDX Missing Member
Mode=Error;Optimize Response=3;Cell Error Mode=TextValue"
Connection = Dispatch('ADODB.Connection')
Connection.Open(ConnStringInternal)
Я надеялся, что смогу инициализировать экземпляр Excel, затем инициализировать модель PowerPivot, а затем создать соединение, используя внутреннюю строку соединения, которую Excel использует для встроенных данных PowerPivot (аналогично тому, как копировать данные powerpivot в книгу Excel в виде таблицы).? - обратите внимание, что строка подключения отличается от той, что я использовал в другом месте). К сожалению, это не работает, и я предполагаю, что Python запускает процесс ADODB.Connection в отдельном экземпляре (поскольку я получаю то же сообщение об ошибке, когда выполняю последние три строки без предварительной инициализации Excel и т.д.):
com_error: (-2147352567, 'Exception occurred.', (0, u'Microsoft OLE DB Provider for SQL
Server 2012 Analysis Services.', u'Either the user, ****** (masked), does not have access
to the Microsoft_SQLServer_AnalysisServices database, or the database does not exist.',
None, 0, -2147467259), None)