小结
我们希望本文能涉及Microsoft Analysis Services 2005可编程挖掘模型的所有重要的内容。但是MAS2005这个产品目前还没有完全完成,很多特性也将跟随时间的变迁而改变。MSDN的SQL站点包括SQL Server 2005 的常用信息,并且特别包括了数据挖掘的内容。
数据挖掘团队的站点也正在不停的更新着最新的信息、诀窍和技巧、白皮书、以及可下载的代码示例。
数据挖掘新闻组,microsoft.public.sqlserver.datamining 和 microsoft.private.sqlserver2005.analysisservices.datamining,也一直在关注着数据挖掘团队,所以它们也拥有大量有用的信息。
附录 1:Microsoft Analysis Services 2005服务器上的常用操作以及请求的协议格式
发送到Microsoft Analysis Services 2005的消息结构.
下面的例子包含一些常用的分析服务请求,使用SOAP、XMLA、以及DDL或 DMX来标示它们。
例1. 使用纯XMLA来发现服务器上的数据库
SOAP <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
SOAP <Body>
XMLA <Discover xmlns="urn:schemas-miscrosoft-com:xml-analysis">
XMLA <RequestType>DBSCHEMA_CATALOGS</RequestType>
XMLA <Restrictions>
XMLA <RestrictionList/>
XMLA </Restrictions>
XMLA <Properties />
XMLA </Discover>
SOAP </Body>
SOAP </Envelope>
例2.在服务器上创建新的挖掘模型。使用XMLA执行(Execute)命令,中间使用DDL语句
SOAP <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
SOAP <Body>
XMLA <Execute xmlns="urn:schemas-microsoft-com:xml-analysis">
XMLA <Command>
DDL <Alter
DDL AllowCreate="true"
DDL ObjectExpansion="ExpandFull"
DDL xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"
DDL >
DDL <Object>
DDL <DatabaseID>TT</DatabaseID>
DDL <MiningStructureID>TestModel</MiningStructureID>
DDL </Object>
DDL <ObjectDefinition>
DDL <MiningStructure
DDL xmlns:xsd="http://www.w3.org/2001/XMLSchema"
DDL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
DDL <ID>TestModel</ID>
DDL <Name>TestModel</Name>
DDL ...
DDL </MiningStructure>
DDL </ObjectDefinition>
DDL </Alter>
XMLA </Command>
XMLA <Properties />
XMLA </Execute>
SOAP </Body>
SOAP </Envelope>
例 3.在挖掘模型上执行一个DMX查询
SOAP <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
SOAP <Body>
XMLA <Execute xmlns="urn:schemas-microsoft-com:xml-analysis">
XMLA <Command>
XMLA <Statement>
DMX SELECT Cluster() FROM CL
XMLA </Statement>
XMLA </Command>
XMLA <Properties />
XMLA </Execute>
SOAP </Body>
SOAP </Envelope>
附录 2:在Ado.NET 和 C++中使用 OLE DB
非托管 C++
非托管C++是使用OLE DB提供程序的常见开发环境。使用它需要具有一些COM知识和对OLE DB模型很好的理解。ATL使用者模版将复杂的COM隐藏起来,并提供了一个编写应用程序的简单方法。使用ATL使用者模版并不像使用分析服务的特定API(如ADOMD.NET)那么容易,但是它是一个能获得非托管应用程序的方法。这里,我们假定用户已经拥有了ATL库的知识以及对数据挖掘的OLE DB规范有了很好的理解,所以我们直接给出一些非托管C++代码。
在下面的代码中,我们假设我们已经有了一个预先设置好的宏CHECK_ERROR,它用来管理COM错误。
一个非常简单(虽然不是非常有用)的执行这个宏的方法是:
#define CHECK_ERROR(x) hr=(x);if(FAILED(hr)) return 1;
下面,我们将给出与OLE DB一节中通过ADO来执行的相同的任务的C++代码,因为篇幅的原因不再给出参数部分的代码。
我们这样初始化一个数据源和一个对话:
CoInitialize(NULL);
{
HRESULT hr;
CDataSource dataSrc;
CSession session;
LPOLESTR szConnStr =
L"Provider=MSOLAP.3; Data Source=localhost;"
L"Initial Catalog=MyCatalog";
CHECK_ERROR(dataSrc.OpenFromInitializationString(szConnStr));
CHECK_ERROR(session.Open(dataSrc));
请注意程序开始处必须要调用CoInitialize(NULL)。CoInitialize 初始化COM库,它必须在试图装载OLE DB提供程序前被当前线程所调用。
ATL 使用者模版提供一个常用的访问发现(Discovery)计划的方法:
CRestrictions
CDynamicAccessor, 1,
&DMSCHEMA_MINING_MODELS> schemaModels;
schemaModels.SetBlobHandling(DBBLOBHANDLING_NOSTREAMS);
CHECK_ERROR( schemaModels.Open( session, _T("MyCatalog") ));
CHECK_ERROR( schemaModels.MoveFirst() );
do
{
if( hr == S_OK )
{
wchar_t* szModelName = NULL;
if( schemaModels.GetValue((DBORDINAL)3, &szModelName) )
{
printf("%S\n", szModelName);
}
}
hr = schemaModels.MoveNext();
}while(hr == S_OK);
DMSCHEMA_MINING_MODELS 是被分析服务使用的挖掘模型提供程序指定计划的GUID。它在oledbdm.h文件中定义,这个文件是数据挖掘的OLE DB规范。上例中开始处用到的CRestrictions 类使用一个通用访问器(CDynamicAccessor)来自动绑定由发现操作返回的所有的列。
MoveFirst/MoveNext/GetValue操作在功能上与我们在OLE DB一节中所介绍的ADO的相应功能非常相似。
如果一个指定计划在一个应用(或多个应用)中被多次使用,我们需要定义一个专用访问器来简化和优化数据访问。下面的这段代码展示了这样的专用访问器是什么样子的,并且为之前用到的通用CRestrictions定义了一个用来发现挖掘模型的专用版本。挖掘模型计划访问器CMiningModelInfo应该完全填充挖掘模型计划感兴趣的所有的列。
class CMiningModelInfo
{
public:
// 构造函数
CMiningModelInfo ()
{
memset(this, 0, sizeof(*this));
}
// 属性
TCHAR m_szCatalog[129];
TCHAR m_szSchema[129];
TCHAR m_szName[129];
TCHAR m_szType[129];
GUID m_guidModel;
TCHAR m_szDescription[129];
...
TCHAR m_szService[129];
...
// Binding Map
BEGIN_COLUMN_MAP(CMiningModelInfo)
COLUMN_ENTRY(1, m_szCatalog)
COLUMN_ENTRY(2, m_szSchema)
COLUMN_ENTRY(3, m_szName)
COLUMN_ENTRY(4, m_szType)
COLUMN_ENTRY(5, m_guidTable)
COLUMN_ENTRY(6, m_szDescription)
...
COLUMN_ENTRY(10, m_szService)
...
END_COLUMN_MAP()
};
typedef CRestrictions<CAccessor<CMiningModelInfo>, 7,
&DMSCHEMA_MINING_MODELS > CMiningModels;
根据数据挖掘OLE DB规范,这里的7是一个挖掘模型计划中的约束值。
下面我们使用上述类,并且使用CMiningModels 来代替通用CRestrictions<CDynamicAccessor>。这个例子中获取模型名称的这段代码:
wchar_t* szModelName = NULL;
if( schemaModels.GetValue((DBORDINAL)3, &szModelName) )
printf("%S\n", szModelName);
将改变成这句代码:
printf("%S\n", schemaModels.m_szName);
我们可以使用相同的动态访问机制(或者为常用任务设计一个专用的访问器)来执行命令(DDL语句或者DMX语句)。
这样初始化命令:
CStringW strDMXStmt;
strDMXStmt = L"SELECT NODE_CAPTION FROM DecisionTree1.CONTENT";
CCommand<CDynamicAccessor> cmdDMX;
cmdDMX.SetBlobHandling( DBBLOBHANDLING_NOSTREAMS );
CHECK_ERROR( cmdDMX.Open( session, strDMXStmt.GetBuffer(), NULL) )
CCommand 类的Open 方法传输一个字符串作为第二个参数。当连接到Microsoft Analysis Services 2005时,这个字符串可以是DMX语句也可以是DDL语句。当命令被执行的时候(Open方法),它的响应格式可以被GetColumnInfo 检测到。这就使得之后的pCol 参数指向了一个包含每一列(如名称、大小、类型)的向量。可以通过这个向量来识别得到的列的顺序。
DBORDINAL ulColumns = 0;
DBCOLUMNINFO* pCol=NULL;
LPOLESTR ppStrings = NULL;
CHECK_ERROR( cmdDMX.GetColumnInfo(&ulColumns, &pCol, &ppStrings) );
定义了目标列(在上述代码中是字符型的列0)以后,我们就可以遍历服务器响应来获取有用的数据了。
wchar_t* pszBuffer = NULL;
CHECK_ERROR( cmdDMX.MoveFirst() );
do
{
wchar_t* szNodeCaption = NULL;
if( cmdDMX.GetValue((DBORDINAL)1, &szNodeCaption) )
{
printf("%S\n", szNodeCaption);
}
hRet = cmdDMX.MoveNext ();
}while(hRet == S_OK );
如果查询返回的是一个嵌套表,每一行将成为一个IRowset 对象。IRowset的子对象可以通过IParentRowset接口由最上层的行集合获得,IParentRowset接口是分层行集合必须提供的接口。
我们假设DMX查询语句是这样的:
"SELECT NODE_CAPTION, NODE_DISTRIBUTION FROM DecisionTree1.CONTENT"
循环读取这个行集合的数据的方法如下所示。例如,我们要读嵌套行集合NODE_DISTRIBUTION的第2列的值,根据数据挖掘OLE DB规范,NODE_DISTRIBUTION行集合必须包含一个挖掘属性值VARIANT。
// 外层循环,读最上层行集合
do
{
// 获得最上层行集合的IParentRowset接口
CComPtr<IParentRowset> spParentRowset;
CHECK_ERROR(cmdDMX.m_spRowset.QueryInterface( &spParentRowset));
// 使用一个动态访问器来读取嵌套行集合
CAccessorRowset<CDynamicAccessor> nestedRowset;
nestedRowset.SetBlobHandling( DBBLOBHANDLING_NOSTREAMS );
// 得到指向nestedRowset变量中的子行集合的指针
CHECK_ERROR( spParentRowset->GetChildRowset(
NULL,
(DBORDINAL)2,
nestedRowset.GetIID(), (IUnknown**)nestedRowset.GetInterfacePtr() ) );
// 将动态访问器绑定到子行集合
CHECK_ERROR( nestedRowset.Bind() );
CHECK_ERROR( nestedRowset.MoveFirst() );
// 内层循环,为了遍历嵌套子集合
while (hr == S_OK )
{
VARIANT varTmp;
::VariantInit( &varTmp );
if( nestedRowset.GetValue((DBORDINAL)2, &varTmp) )
{
// 在这里使用 VARIANT
}
// 继续执行内层循环
CHECK_ERROR( nestedRowset.MoveNext() );
}
// hr不是OK时,在以下两种情况下不是错误:
// DB_S_ENDOFROWSET 或显示了额外的确认代码
// 继续执行外层循环
hr = cmdDMX.MoveNext ();
}while(hr == S_OK );
参数nestedRowset是CAccessorRowset<CDynamicAccessor>类型的,它自动绑定所有的列,并且它可以方便的访问嵌套行集合所有字段的值。它提供了方法MoveFirst、MoveNext和GetValue,这些方法与cmdDMX命令非常相似。这是因为CCommand 类是源自CAccessorRowset 类的。它们将不同的行集合包含在一起: cmdDMX 对象包含命令执行结果的上层行集合,nestedRowset 对象包含子行集合。
设置OLE DB命令参数的详细内容,请到MSDN上查找,MSDN上提供了很多相关的例子和文档。
ADO.NET
ADO.NET被设计来在托管应用中代替ADO。它可以在托管应用中非常方便和容易的使用。ADO.NET是从托管应用连接到Microsoft Analysis Services 2005的首选解决方案,因为它是专为托管应用所设计的。ADO.NET一个非常重要的局限性是它不支持命名参数。也就是说,它只支持通过参数的序号来访问它们;所以ADO.NET不能使用带参数的DMX语句。
使用ADO.NET,需要在托管项目开始位置添加System.Data.dll引用,并在代码中这样使用这个引用:
using System.Data.OleDb;
像我们之前提过的一样,当使用单个连接将OLE DB对象数据源和对话打包在一起的时候, ADO.NET与ADO非常相似。要使用ADO.NET,需要先初始化一个ADO.NET连接,并使用OLE DB连接字符串来配置它:
OleDbConnection conn = new OleDbConnection();
conn.ConnectionString = "Provider=MSOLAP.3; Data Source=localhost; " +
"Initial Catalog=MyCatalog";
conn.Open();
一旦初始化连接完毕,就可以使用这个连接来发现服务器上的各种对象了。下面的代码是用来获取分析服务器上的挖掘模型列表的:
Guid miningModels = new Guid("{3ADD8A77-D8B9-11D2-8D2A-00E029154FDE}");
object[] arRestrictions = new object[1];
arRestrictions[0] = "CheckInTestDB";
DataTable tblModels =
conn.GetOleDbSchemaTable(miningModels, arRestrictions);
foreach( DataRow row in tblModels.Rows )
{
string modelName = row["MODEL_NAME"];
// 在这里使用modelName
}
在这段代码中,我们使用一个约束,目录名称,来限制在挖掘模型的目录MyCatalog 中的发现操作。DMX语句和DDL语句可以通过命令来执行,与ADO使用的方法非常相似。然而,ADO.NET命令(Command)对象给出了执行(Execute )方法的多个版本。
其中之一就是ExecuteReader。ExecuteReader返回一个Data Reader对象(执行.NET IDataReader接口)。数据阅读器可以用来遍历服务器上的表型结果。如果这个表型结果包含嵌套表,可以为每一行获得一个数据阅读器。下面的代码执行一个DMX查询,它将返回上层中的列(NODE_CAPTION)和被嵌套表中的列(NODE_DISTRIBUTION)。然后它遍历上下两层的结果,这段代码体现了它是如何从服务器响应中获取信息的:
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT NODE_CAPTION, NODE_DISTRIBUTION FROM " +
" DecisionTree1.CONTENT WHERE NODE_TYPE=2";
OleDbDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
// 使用上层字段
Console.WriteLine(rdr.GetString(0));
// 控制嵌套阅读器
// 首先,确认列1种的对象是一个嵌套阅读器
object objNestedRowset = rdr.GetValue(1);
Debug.Assert(objNestedRowset is IDataReader);
OleDbDataReader nestedReader = (OleDbDataReader)objNestedRowset;
// 现在,遍历嵌套阅读器
while (nestedReader.Read())
{
Console.WriteLine(nestedReader.GetValue(1).ToString() );
}
}
与ADO命令非常相似, ADO.NET的 OleDbCommand 对象必须使用活跃连接(Connection)。它的CommandText属性可以被DDL语句或者DMX语句赋值。在ADO.NET阅读器对象(OleDbDataReader)中,根据列的序号来访问列。在列值类型已知的情况下,可以根据类型重新获得列值,就像上述程序中“使用上层字段”部分的代码。列值也可以作为普通对象而重新获得。在这个例子中,对象的类型可以在重新获得后被赋值,如上述代码段中“控制嵌套阅读器”中的代码所示。
另一种执行(Execute)方式是ExecuteNonQuery。这个方式在执行DDL语句或DMX语句后,不希望有返回值的时候特别适用(而不是返回成功或失败),例如CREATE MINING MODEL 或者 INSERT INTO。在下面的代码中,使用 ExecuteNonQuery 来执行DDL 处理(Process)语句。
cmd.CommandText = " <Process " +
"xmlns=\"http://schemas.microsoft.com/analysisservices/2003/engine\">"+
" <Type>ProcessStructure</Type>"+
" <Object>"+
" <DatabaseID>CheckInTestDB</DatabaseID>"+
" <MiningStructureID>Structure1</MiningStructureID>"+
" </Object>"+
" </Process>";
cmd.ExecuteNonQuery();
与ADO非常相似,如果语句执行失败,将报一个异常。C# 异常处理允许开发者获得这个异常并研究产生错误的原因。
RSS订阅






收 藏
推 荐