当我试图使用Python从PowerPivot模型中读取一些数据时,一个看似微不足道的任务变成了一个真正的噩梦。基本上,我想要做的就是通过以下方法之一连接到底层的PowerPivot(即MS Analysis Services)引擎,列出模型中包含的表,然后使用简单的DAX查询从每个表中提取原始数据(类似于EVALUATE(table_name)
)。但事实证明,这可能并不容易。
0. 一些背景信息
如您所见,我尝试了几种不同的方法。我将尽可能仔细地记录所有内容,以便那些不熟悉PowerPivot功能的人对我的要求有一个好的理解。
首先,一些关于编程访问Analysis Services引擎的背景知识(它说是2005 SQL Server,但所有内容应该仍然适用):SQL Server数据挖掘可编程性和用于Analysis Services连接的数据提供程序。
我将在下面的示例中使用的示例Excel / PowerPivot文件可以在此处找到:Microsoft PowerPivot for Excel 2010和PowerPivot in Excel 2013 Samples。
另外,请注意我使用的是Excel 2010,因此我的一些代码是特定于版本的。例如,如果您使用Excel 2013,则wb.Connections [“PowerPivot Data”] .OLEDBConnection.ADOConnection
应改为wb.Model.DataModelConnection.ModelConnection.ADOConnection
。
我将在本问题中始终使用的连接字符串基于此处找到的信息:用C#连接到PowerPivot引擎。此外,某些方法显然需要在检索数据之前对PowerPivot模型进行某种初始化。请参见:这里:从VBA自动刷新PowerPivot操作。
最后,这里有几个链接,显示这应该是可以实现的(但请注意,这些链接主要是指C#,而不是Python):
- 连接PowerPivot DataModel后,如何用数据填充数据集?
- 使用C#连接PowerPivot
- 2013 C#连接到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()
这与使用Python或IronPython访问MSSQL的最简单方法是什么?类似。不幸的是,这也不起作用:
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. 使用adodbapi模块来使用ADO
import adodbapi
ConnString = "Provider=MSOLAP;Data Source=$Embedded$;Locale Identifier=1033;
Location=H:\\PowerPivotTutorialSample.xlsx;SQLQueryMode=DataKeys"
Connection = adodbapi.connect(ConnString)
与《Python和MS Access VBA之间OLEDB/ODBC相反的工作方式》类似,我遇到的错误是:
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. 通过Excel/win32com模块使用ADO
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的博客文章:Export a table or DAX query from Power Pivot to CSV using 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. 通过 Excel/win32com 使用 ADO,直接使用 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连接Access [重复]和在Win32平台上使用ADO查询Access数据库(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. 通过Excel/win32com使用ADO,直接使用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)
SELECT * FROM [Model].[table] WHERE [Model].[table].[RowNumber]>=1000 AND [Model].[table].[RowNumber]<=2000
。RowNumber似乎是一个隐藏的内部列。为了使其正常工作,必须将以下内容附加到连接字符串中:SQLQueryMode=DataKeys
。这是我之前编写的用于提取PowerPivot数据的Excel“工具”:http://github.com/akavalar/ExtractPowerPivot - akavalar