作者: Bogdan Crivat,微軟公司
時間:2005年3月
适用于:
微軟 SQL Server 2005
SQL Server 資料挖掘(SQL Server Data Mining)
摘要:介紹SQL Server 2005資料挖掘的新API以及幾種常用的開發場景。
版權
在這篇文章中所包含的資訊代表了從釋出日起微軟對所讨論的問題的目前觀點。因為微軟必須對市場的變換做出響應,它不應該被了解為微軟所必須承擔的任務的一部分,微軟也不能保證在釋出日之後所提出的資訊的精确性。
這個白皮書僅僅是為了資訊的目的,微軟對本文中的資訊不做任何授權、表示、暗含或規定。
依從所有可适用的版權法是使用者的責任。沒有限制權利在版權之下,這個文檔的部分不允許被再生産,存放或介紹入檢索系統, 或被以任何形式傳送或通過任何手段(電子, 機械, 影印, 記錄, 或其他) 或為任何目的,沒有微軟的明确書面允許。
微軟對于在這篇文章中所包含的主題擁有專利、專利申請、商标、版權或其他的一些知識産權。除了微軟明确提供的一些書面的特許契約,這個文檔的并不提供給您任何專利、商标、版權或其他知識産權的執照。
版權所有2005 Microsoft Corporation。
Microsoft 和Visual Studio在美國或其他國家都有注冊商标或微軟的商标。
在這裡的實際的公司和産品的名字可能是他們各自的商标。
QUOTE:
目錄
概述
與 Microsoft Analysis Services 2005通訊
資料定義語言(DDL) 和 資料挖掘擴充語言(DMX)
資料挖掘任務
通用API
Adomd.NET 作為通用API
OLE DB作為通用API
Analysis Management Objects – AMO
進度通知:使用跟蹤對象
無伺服器的資料挖掘:本地挖掘模型
擴充SQL Server 資料挖掘功能
使用Adomd.NET Server 擴充DMX
插件算法和内容檢視器
應用場景建議
應用場景1:簡單的資料挖掘預測
應用場景2:Web應用——使用伺服器上現有模型的規則
應用場景3:為目前資料在伺服器上建立和訓練一個新的模型
小結
附錄 1:Microsoft Analysis Services 2005伺服器上的常用操作以及請求的協定格式
附錄 2:在Ado.NET 和 C++中使用 OLE DB
非托管 C++
ADO.NET
概述
随着微軟SQL Server 2005的誕生,統計技術和機器學習算法的綜合産物,也就是衆所周知的資料挖掘,被帶入了一個新的階段。在SQL Server 2005中,資料挖掘最重要的轉變是改變了它的目标使用者。除了作為一個科學的實驗工具,面向有限的專業人士,如今SQL Server 資料挖掘已廣泛存在,成為開發者捶手可得的工具,并且已經做好了在更廣闊的領域中應用的準備。從電子資料表格到網絡遊戲,從點到點通訊系統到應用伺服器,絕大多數應用都有這樣一個共同點:它們不得不進行資料處理。在進行資料處理的時候,它們使用特定的标準API來通路資料。在SQL Server 2005資料挖掘的資料處理系統中,這些API也同樣可以被智能的嵌入到應用中。
這篇文章帶你思考将SQL Server資料挖掘嵌入到應用中的契機。文章重點關注可程式設計内容,即通過寫代碼來使用資料挖掘技術和增強伺服器特性。我們将給出一系列可以被資料挖掘引擎執行的常用任務,并針對它們展示微軟SQL Server資料挖掘體系結構的解決方案。之後,我們将列舉一些用戶端産品(Adomd.NET 和 OLE DB)的可程式設計API。之後的一個章節專注于Analysis Management Objects所管理的API。随後,我們将展示伺服器建立新的存儲過程以及添加新的算法插件和檢視器等擴充功能.
資料挖掘應用也可以結合聯機分析處理(OLAP)、Reporting Services或者Integration Services來建立。然而,這篇文章不介紹這些内容,而是嚴格通過代碼來介紹資料挖掘功能和如何增強資料挖掘功能。
資料挖掘和聯機分析處理都是微軟分析服務的元件。本篇文章中介紹的用戶端API同時适用于這兩種元件。但是,本文的介紹僅針對資料挖掘。其中的場景、代碼執行個體、中繼資料對象都是針對資料挖掘的。
與 Microsoft Analysis Services 2005通訊
讓我們從Microsoft Analysis Services 2005(與資料挖掘伺服器之間)的通訊協定開始。用戶端通過Analysis Services 2005來執行這個通訊協定(比如使用Microsoft OLE DB Provider for Analysis Services 9.0 或者 Adomd.NET),并且這個協定也必須被其它用戶端執行。
Microsoft Analysis Services 2005 使用SOAP作為最外層的通訊層。(更多關于SOAP的内容請通路這個網頁。)SOAP為應用程式定義了一系列可以通過XML消息來調用的方法。 這些方法由Microsoft Analysis Services 釋出,使用XML for Analysis或者 XMLA來定義。
XMLA規範是由一個超過20家研究商業智能的龍頭企業(包括微軟公司、Hyperion和SAS 學會)組成的組織提出的,它是一個标準的OLAP和資料挖掘技術接口。 更多關于XMLA的資訊,請點選這裡。
XMLA定義了兩種發送給伺服器的請求,以及伺服器進行響應傳回的資訊的格式。請求的類型是“發現(Discover)”和“執行(Execute)”。“發現”用來從伺服器擷取資訊和中繼資料。例如, “發現”可以用來擷取伺服器上一系列挖掘模型以及它們的屬性(列描述、算法等等)。“執行”用來執行對伺服器的指令,如建立一個新的目錄或者挖掘模型、訓練一個模型、或者執行一個查詢。
資料定義語言(DDL) 和 資料挖掘擴充語言(DMX)
XMLA的“執行”指令可應用于多種任務。這裡有幾個例子:
· 建立(Create)指令:這些指令在伺服器上建立一個新的中繼資料對象,它們包含被建立的對象的全部或部分屬性,例如名稱、資料綁定、挖掘模型和挖掘結構中的列、挖掘模型的資料挖掘算法等等。
· 修改(Alter)指令:這些指令修改已存在的伺服器中繼資料對象的屬性。
· 删除(Drop)指令:這些指令用來從伺服器上去掉中繼資料對象。
· 處理(Process)指令:這些指令用來初始化那些基于目前綁定的訓練資料集定義的中繼資料對象上的訓練序列。
· 語句 (查詢語句)。
當一個XMLA“執行”請求描述一個對象(如建立Create或者修改Alter語句)或者将一個對象定義為一個動作的目标(如處理Process或删除Drop)時,指令的内容由Microsoft Analysis Services 資料定義語言(Data Definition Language —— DDL)組成。DDL是Analysis Services 2005中中繼資料以及中繼資料操作的内部表示方法。 伺服器上的對象存儲在DDL中,商業智能化項目由若幹DDL片段組成。關于DDL的更多細節,請檢視SQL Server 2005 線上文檔中的“分析服務腳本語言(Analysis Services scripting language)”。
另一方面,在資料挖掘任務中,當XMLA請求是一個語句的時候,XMLA “執行”請求使用一種查詢語言——DMX(資料挖掘擴充Data Mining eXtensions語言)作為它的請求的内容。DMX語言在針對資料挖掘的OLE DB 規範中定義,在這裡可以使用。
Microsoft Analysis Service 2005 也可以使用另一種查詢語言來執行語句——MDX。MDX語言是為OLAP元件所設計的。是以,這裡我們隻關注DMX。
區分DDL和DMX是非常重要的:DDL是一種由Analysis Services 2005使用的類XML語言,用來描述、管理和指定中繼資料。DDL可以很友善的擴充到XMLA。
而DMX是一種為資料挖掘而設計出的類SQL語言。DMX與SQL非常類似,DMX包含這樣的結構,它們允許建立和操作中繼資料對象(想想SQL中的CREATE TABLE 或者INSERT INTO語句,DMX中有與其等價的CREATE MINING MODEL 和INSERT INTO 語句)。然而,對于管理中繼資料對象的任務來說,DMX的靈活性比DDL要差。DMX語句無法擴充到XMLA;但是它們可以被固定的XMLA結構所包括。
文章結尾處的附錄1提供更多Microsoft Analysis Services 2005協定部署的細節内容。
SOAP相對來說比較容易操作;大多數開發工具都提供針對建立、傳輸、接收SOAP包的幫助。但是,選擇使用DDL請求還是DMX請求,并把它按照适當的格式封裝到XMLA語句中就不是一件那麼容易的事情了。是以開發者需要比僅僅使用XML流作為通訊方法更好的工具。這也就是為什麼如今可程式設計API存在的原因。這些API将XMLA請求的相關操作作業打包,分析XMLA的響應資訊,并且向開發者提供一個更具邏輯性的通訊視圖,用來操作内部細節。也有一小部分API可以被Microsoft SQL Server 資料挖掘應用。選擇哪個API取決于用戶端執行的請求的類型和用戶端的開發環境。
下一節将分析典型的資料挖掘在用戶端/伺服器端通訊(C/S)時的用戶端請求。之後的章節将從各種資料挖掘可程式設計API處理典型請求時的方法以及它們支援的用戶端環境的角度,具體展示這些API以及它們之間的差别。
資料挖掘任務
SQL Server資料挖掘伺服器支援多種請求。對于每一種請求,我們将給出可用的DDL指令和等價的DMX:
· 中繼資料的發現:這些請求允許用戶端應用根據一些屬性,如挖掘模型的列、使用的算法、被訓練的資料集,疊代伺服器上已存在的中繼資料對象,并且從中選擇一個或多個有用的中繼資料。XMLA規範為這類請求定義了發現(Discover)指令,沒有與其直接對應的DMX或DDL。
· 中繼資料的定義:這些請求是一系列對伺服器上的資料結構的定義。這些資料結構是挖掘結構和挖掘模型,它們由目錄(資料庫)組合在一起。針對這些請求的XMLA指令是執行(Execute),包括DDL用來建立和修改的Create 或 Alter 語句。也可以使用DMX的 CREATE語句,盡管它的靈活性要比DDL 的Create語句差。
· 模型的訓練:這些請求為伺服器端的資料挖掘模型執行訓練處理。訓練可以使用預先綁定的資料(在中繼資料定義時決定),也可以隻描述目前正在處理的操作的資料集。XMLA指令還是 執行(Execute),包含一個DDL的處理(Process)語句。在DMX中,訓練可以由插入(INSERT INTO)指令得到。
· 查詢模型:這些請求包括(但不局限于)在資料挖掘中我們常說的記分(Scoring)和預測(Predicting)。 我們将查詢定義為使用資料挖掘所建立的模型的過程。這些請求由發送到伺服器端的DMX語句組成。
· 訂閱進度通知:這些請求非常友善地顯示一個進度條,這個進度條用來顯示在執行一個很長的操作時或者在指定時間檢查伺服器狀态時的實際進展情況。這類請求不能使用DMX建立。它們擁有跟蹤的功能,作為帶DDL訂閱(Subscribe)語句的XMLA執行(Execute)指令來進行傳輸。
對這些任務有了基本概念後,我們可以正式開始列舉可程式設計API及它們的特性了。上述任務中的最後一項,關于進度的通知,我們将單立一節的内容進行講解,下面的API章節就不再讨論這個任務了。
通用API
我們将探讨兩種常用的API:Adomd.NET和OLE DB。它們的差别在于它們的目标環境不同: OLEDB (OLE for Databases)主要針對非托管應用(盡管它也可以被用于托管應用);而Adomd.NET針對托管應用。下邊我們将看到,這些API具有非常相似的結構。它們都基于非常有名的資料庫通路範例。
Adomd.NET 作為通用API
Adomd.NET是由Microsoft Analysis Services 2000提供的舊ADOMD庫的托管版本。這個庫專門為Microsoft Analysis Services 2005的用戶端而設計。它執行ADO.NET資料通路範例(包括一系列标準接口,如
IDbConnection、IDbCommand、IDataReader等等),并且使用Microsoft Analysis Services 2005提供的許多特性來擴充這些範例。
在.NET架構中,
System.Data命名空間使你可以建立從多個資料源有效管理資料的元件。關于此點的更多内容請參考關于
System.Data 命名空間的MSDN文檔。
System.Data擁有多種特性,它定義一系列可以被.NET資料提供程式執行的用來通路關系型資料庫的接口。微軟分析服務(Microsoft Analysis Services)是一個多元資料庫,它比普通關系型資料庫擁有更強大的功能。Adomd.NET是一個.NET資料提供程式,能執行提供給.NET資料提供程式的一系列标準
System.Data接口,這些接口的功能在Analysis Services 2005中得以增強,也增加了很多在關系型資料庫中不使用的新特性。
Adomd.NET作為SQL Server 2005連接配接元件的一部份被安裝,也可以單獨下載下傳進行安裝。在從應用程式使用它之前,必須在機器上安裝Adomd.NET資料提供程式。
這一節的代碼示例使用Visual Studio 2005的C# 2.0實作。經過一個簡單的“翻譯”過程,它們也可以被Visual Studio 套件的任意托管語言(如Visual Basic .NET或Visual C++ .NET)運轉。
在應用程式中使用 Adomd.NET時,必須先添加一個
Microsoft.AnalysisServices.AdomdClient.dll程式集,使用類似如下的代碼進行添加:
using Microsoft.AnalysisServices.AdomdClient;
下一步,連接配接Microsoft Analysis Services 2005 伺服器:
AdomdConnection
conn = new
AdomdConnection
();
conn.ConnectionString = "Data Source=localhost; " +
"Initial Catalog=MyCatalog";
conn.Open();
在資料挖掘任務清單中,第一個是中繼資料對象的發現。就像我們前邊所說的一樣,中繼資料的發現由XMLA發現(Discover)指令實作。Adomd.NET提供兩種簡單的方法來發現中繼資料。第一種與AMO非常相似:
foreach (MiningModel model in conn.MiningModels)
{
Console.WriteLine(model.Name);
}
微軟分析服務開發團隊盡了很大的努力來确認常用的計劃都已包含于Adomd.NET Connection類所釋出的程式集中。你可以使用之前的這些代碼來通路資料庫中的挖掘結構集、一個結構中或者整個資料庫中的挖掘模型清單、以及模型和結構中的列。Adomd.NET 擁有遠超過AMO 的功能,它允許代碼這樣構成分級模型:
foreach (MiningContentNode node in model.Content)
{
foreach( MiningContentNode in node.Children )
{
// 使用節點的屬性
}
}
對Microsoft Analysis Services 2005中的OLAP計劃來說,Adomd.NET Connection 對象所釋出的程式集比資料挖掘所提供的程式集更加适用。是以在Adomd.NET中你可以使用相同的方法來浏覽立方體和次元。
發現伺服器中繼資料的第二種方法與OLE DB的方法非常相似,也就是執行一個發現(Discovery)指令來傳輸一個GUID計劃和一系列限制,然後傳回一個可以被周遊的表型結果。 有些計劃傳回的是分級表型結果(嵌套表)。這也就是為什麼在Adomd.NET 中,一個發現操作的傳回值是一個
資料集(DataSet);對比ADO.NET ,它傳回的是一個資料表。資料集可以描述表之間的關系,是以資料集可以包含一些發現操作的嵌套表型結果。下邊的一段代碼片段發現了一個挖掘模型的内容,并且使用資料集得到了響應NODE_DISTRIBUTION嵌套表計劃的嵌套行:
Guid modelContent = new Guid("{3ADD8A76-D8B9-11D2-8D2A-00E029154FDE}");
object[] arRestrictions = new object[3];
// 限制發現(Discovery)MyCatalog目錄中DecisionTree1模型的内容。
// 第二個限制(MODEL_SCHEMA) 在這裡忽略。
arRestrictions[0] = "MyCatalog";
arRestrictions[1] = null;
arRestrictions[2] = "DecisionTree1";
DataSet dsContent = conn.GetSchemaDataSet(
modelContent, arRestrictions);
// 一緻性檢查:确認關系值為1
Debug.Assert( dsContent.Relations.Count == 1);
DataRelation relation = dsContent.Relations[0];
DataTable topTable = relation.ParentTable;
foreach (DataRow row in topTable.Rows)
{
// 使用最上層表中的列
Console.WriteLine("NODE_CAPTION=" + row["NODE_CAPTION"]);
Console.WriteLine("NODE_UNIQUE_NAME" + row["NODE_UNIQUE_NAME"]);
// 根據關系描述,取得嵌套行
DataRow[] distRows = row.GetChildRows(relation);
foreach (DataRow distRow in distRows)
{
// 使用嵌套表中的列
Console.WriteLine(distRow["ATTRIBUTE_VALUE"]);
}
}
與OLE DB非常相似,Adomd.NET提供發送請求到伺服器的指令。這些請求可以是DDL或者DMX的;是以它們可以建立或者修改已經存在的、用來訓練挖掘結構和挖掘模型的中繼資料對象(DDL和DMX),也可以查詢已存在的模型(隻支援DMX)。我們也就可以通過此點來區分中繼資料的建立、處理,和DMX的查詢。它們的差別在于:DMX查詢傳回一個表型結果(分層的或者單層的),而中繼資料建立和處理語句隻傳回一個成功或者失敗。Adomd.NET公開了一些執行(Execute)方法,其中兩種對于傳回成功/失敗的請求和傳回表格型資料的請求非常有用。
第一種:我們使用一個DDL處理語句作為示例,任何其它的DDL語句(如Alter、Create或者Drop)也可以使用相同的代碼。
AdomdCommand cmd = new AdomdCommand();
cmd.Connection = conn;
cmd.CommandText = "
"xmlns=/"http://schemas.microsoft.com/analysisservices/2003/engine/">"+"<Type>ProcessDefault</Type>" +
" <Object>" +
" <DatabaseID>MyCatalog</DatabaseID>" +
" <MiningStructureID>Structure1</MiningStructureID>" +
" </Object>" +
" </Process>";
cmd.ExecuteNonQuery();
同樣,ExecuteNonQuery也可以執行一個DMX語句來産生相同的結果:
"INSERT INTO MINING STRUCTURE Structure1"
當 ExecuteNonQuery被調用的時候,如果請求執行成功了,語句将傳回;如果執行失敗将報一個異常。在之後的例子中,将給出異常的具體資訊,如伺服器傳回的錯誤資訊。
第二種執行方法是ExecuteReader。這種方法應該在語句的傳回值肯定是表型的時候被調用。就像我們前邊所提到的一樣,Microsoft Analysis Services 2005傳回的結果有時會是分層表型結果。例如,讓我們考慮這樣的一個模型, Model1,它根據消費者的年齡、他/她汽車的顔色來預測消費者的性别和購買商品的清單。當然,這樣的模型可能并沒有現實意義,但是,它卻是一個很容易使DMX語句傳回分層表結構結果的例子。
下面的代碼使用 AdomdCommand 來傳輸一個預測查詢給伺服器,并讀取最上層傳回值(預測的性别)和嵌套傳回值(預測的商品清單):
cmd.CommandText = "SELECT Gender, Products FROM Model2 " +
"NATURAL PREDICTION JOIN " +
"( SELECT 30 AS Age, 'Black' as CarColor) AS T";
AdomdDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
// 使用上層結果,預測性别
Console.WriteLine( rdr.GetString(0) );
// 為第一列的嵌套内容獲得一個nested reader
AdomdDataReader nestedReader = rdr.GetDataReader(1);
// 讀嵌套内容并使用嵌套資料
while (nestedReader.Read())
{
Console.WriteLine(nestedReader.GetString(0));
}
nestedReader.Close();
}
調用 GetDataReader 将傳回一個新的執行個體AdomdDatareader 類,它被初始化後用來通路對列編号了的嵌套表。如果指定的列并不是一個被嵌套的表,“執行”方法将傳回一個異常。我們可以使用類似如下代碼的語言來判斷Datareader中的列是不是一個嵌套表:
if (rdr.GetFieldType(1) == typeof(AdomdDataReader) )
{
// 如果進入If循環,rdr的第一列是一個嵌套表
}
在現實生活中,最有可能根據使用者的實際輸入來進行預測。一個常見的Web應用場景是這樣的,使用者給出年齡和汽車的顔色,應用程式代碼産生一個DMX請求來預測該客戶購買商品的清單。當開發者對DMX和相關的資料類型有一個很好的了解的時候,查詢可以使用将客戶錄入的所有字元串連接配接起來的方式進行建立。 然而,這樣的方法具有潛在的威脅,它可能引起DMX嵌入錯誤或研發錯誤。一種更好的方式是使用參數來建立查詢,将使用者輸入的内容作為參數的值。Adomd.NET對DMX的參數化查詢提供了大量的支援,就像下面的例子一樣。這段代碼不包括讀取傳回值的内容,因為它與之前的代碼片段是一樣的。
cmd.CommandText = "SELECT Gender, Products FROM Model2 " +
"NATURAL PREDICTION JOIN " +
"( SELECT @Age AS Age, @Color as CarColor) AS T";
AdomdParameter paramAge, paramColor;
paramAge = cmd.CreateParameter();
paramAge.Direction = ParameterDirection.Input;
paramAge.ParameterName = "Age";
cmd.Parameters.Add(paramAge);
paramColor = cmd.CreateParameter();
paramColor.Direction = ParameterDirection.Input;
paramColor.ParameterName = "Color";
cmd.Parameters.Add(paramColor);
cmd.Parameters["Age"].Value = 30; // 使用者在這裡輸入
cmd.Parameters["Color"].Value = "Black"; // 使用者在這裡輸入
AdomdDataReader rdr = cmd.ExecuteReader();
請注意,參數不一定非是數值。請考慮這樣的DMX語句:
INSERT INTO MyModel(Col1, Col2)
OPENQUERY(DataSource, "SELECT ColA, ColB FROM Table") AS T
或者是:
SELECT PREDICT(Products, 5) FROM MyModel NATURAL PREDICTION JOIN
OPENQUERY(DataSource, "SELECT ColA, ColB FROM Table") AS T
這兩個語句都用了一個OPENQUERY 函數來描述執行時被伺服器使用的表型内容(訓練挖據模型,分别進行預測)。使用Adomd.NET,我們可以使用一個參數來取代OPENQUERY函數,并且向伺服器傳輸一些表型内容。查詢可能是這樣的:
INSERT INTO MyModel(Col1, Col2)
@MyTabularContent AS T
或者是:
SELECT PREDICT(Products, 5) FROM MyModel NATURAL PREDICTION JOIN
@MyTabularContent AS T
在這些例子中, 參數MyTabularContent可以是一個資料表(DataTable)或者執行一個IDataReader .NET接口。相比來說,資料表更容易使用,IDataReader接口具有不需要将所有資料儲存在記憶體中的優勢。IDataReader執行的一個例子是用戶端應用執行的SQL查詢所傳回的SqlDatareader。當然,一個SQL查詢請求也可以在OPENQUERY 功能中被送出,但是這種請求的前提是伺服器已經通路到了SQL資料庫。表型參數為兩種情況所設計:
· 資料庫中的資料是用戶端可見但是伺服器端不可見的(這種情況應該使用IDataReader)
· 資料在用戶端應用程式所占用的記憶體中(DataTable可以在這種情況時使用)
Adomd.NET 一個獨有的特點是可以使用XML傳回標明的列。Microsoft Analysis Services 2005為指定的查詢傳回這樣的列。例如:
SELECT * FROM MyModel.PMML
這個語句将傳回PMML 2.1格式下MyModel 模型的内容(即被支援PMML的MyModel使用的資料挖掘算法)。除了例如模型名稱、PMML緩沖區大小這類元資訊外,這個語句的傳回值還包括指定的列和包含PMML的MODEL_PMML(一種被資料挖掘團隊設計出來的XML格式,用來描述挖掘模型的内容)。
這個XML的内容可以以字元串方式被通路,但是Adomd.NET擁有分析XML列的能力(基于伺服器端發來的指定的列類型來判斷) 并且将它們釋出為一個System.Xml.XmlReader對象。這些列也可以作為字元串被讀取。以下的示例代碼使用這一特性來通路帶XML目錄的列,同時使用字元串和XmlReader兩種方法。
cmd.CommandText = "SELECT * from [MyModel].PMML";
AdomdDataReader rdr = cmd.ExecuteReader();
// 周遊響應結果,讀取第5列,MODEL_PMML
while (rdr.Read())
{
// 取得列值
object objXMLValue = rdr.GetValue(5);
if (objXMLValue is System.Xml.XmlReader)
{
//使用一個XML reader
System.Xml.XmlReader pmmlReader =
(System.Xml.XmlReader)objXMLValue;
//在這裡讀PMML
}
// 使用字元串來擷取這列
string strPMMLAsString = rdr.GetString(5);
}
Adomd.NET 的另一個特性是連續通路伺服器響應,将在 進度通知:使用跟蹤對象 一節中具體闡述。
Adomd.NET是Microsoft Analysis Services 2005中最具靈活性,最容易使用的用戶端API。在編寫以微軟分析服務為目标的.NET應用操作時也同樣推薦使用它。它提供了類AMO通路伺服器中繼資料的大量特性,它允許執行DDL和DMX語句,并且它對DMX語句的參數也有很好的支援。
OLE DB作為通用API
在微軟Windows作業系統中,OLE DB是進行資料通路最常用的API。OLE DB是針對OLE對象的規範。這些OLE對象有一系列标準接口。這些接口的标準化使得一個程式設計模型幾乎可以通路所有類型的資料源。微軟SQL Server資料挖掘有一個OLE DB提供程式。這個提供程式能将OLE DB程式設計模型翻譯成資料挖掘所需要的内容。
關于 OLE DB的更多内容,請通路MSDN OLE DB頁。
OLE DB可以被很多種程式設計語言直接使用。在本機(非托管)C++中,可以建立OLE DB對象,并且可以直接調用OLE DB的方法。ALT使用者模版(ALT Consumer Templates)也為OLE DB使用者應用程式提供了一系列有用的類。在Visual Basic或者VBA中,OLE DB提供程式可以在ADO(ActiveX Data Objects,ActiveX資料對象)中使用。在托管語言中,我們可以使用ADO.NET 庫來調用OLE DB提供程式。
OLEDB程式設計模型以如下對象為中心。首先,資料源必須由伺服器端初始化和控制。其次,在這個資料源上必須初始化一個通訊對話。OLE DB架構的第三個重要組成部分就是指令,它們将伺服器請求打包。每個伺服器請求都是對話的一部分。OLE DB架構的第四個組成部分是伺服器響應。大多數資料挖掘任務可以被OLE DB指令所執行。例如,可以使用一個指令來傳輸DMX語句,也可以傳輸一個DDL語句來建立一個新的中繼資料。但是指令并不能傳輸一個發現中繼資料語句(Discovery)。是以針對此點,OLE DB定義了一個可以被對話執行的IDBSchemaRowset接口。這裡我們需要注意的是,ADO和ADO.NET有一個封裝了資料源和OLE DB對話的連接配接對象(Connection)。這兩種标準的OLE DE對象(資料源和OLE DB對話)公開的功能在ADO和ADO.NET的連接配接對象中可用。
所有的OLE DB對象(資料源、對話、指令和響應)經常在COM元件中執行來調用OLE提供程式。
下面的圖1給出了一個OLE DB解決方案的架構:
圖 1. OLE DB解決方案架構圖
如圖中所示,首先,各個用戶端上安裝的OLE DB提供程式使用OLE DB來連接配接伺服器。Microsoft Analysis Services 2005的OLE DB提供程式是在安裝SQL Server 2005的連接配接元件時安裝的。
OLEDB連接配接經常通過連接配接字元串的方法來初始化。連接配接字元串包含一系列有分号分隔開的“名字-值”字元對,即屬性。這些屬性描述了OLE DB連接配接初始化時的各個參數。所有的OLE DB包(如ATL使用者模版、ADO、或者ADO.NET)都根據連接配接字元串來進行初始化。這裡給出一個用來連接配接Microsoft Analysis Services 2005的OLE DB連接配接字元串的例子:
"Provider=MSOLAP; Data Source=localhost; "
第一個屬性 Provider描述示例中指定的提供程式。
"MSOLAP" 是微軟分析服務中OLE DB提供程式的名字。請注意這個名字是依賴版本的。如果一台機器上安裝了多個分析服務OLE DB提供程式(如一個來自Analysis Services 2005,一個來自Analysis Services 2000),就需要使用更加準确的名字來區分版本:Analysis Services 2000使用“MSOLAP.2”,Analysis Services 2005使用“MSOLAP.3”。第二個屬性“Data Source”表示要連接配接的資料源。它一般都是一個機器名,但是也可以是其它表達方式。例如,它可以是一個檔案名,或者是一個網絡URL,如我們将在下文“HTTP Pump”章所見到的一樣。關于分析服務OLE DB提供程式屬性的更多資訊,你可以從SQL Server線上資訊中得到。
下面這段代碼是一個VBA應用程式中使用OLE DB的例子,它使用了ADO。具有相似功能的C#代碼(使用ADO.NET)和非托管C++代碼在附錄2中。
為使用ADO,需要為你的Visual Basic (或者VBA)工程添加一個指向Microsoft ActiveX Data Object庫(你機器中最新版本的庫)的引用。以下的代碼片段是在2.8版本上進行測試的:
1 Dim Conn As ADODB.Connection
2
3 Set Conn = New ADODB.Connection
4 Conn.Open ("Provider=MSOLAP.3; Data Source=localhost;" _
5 & "Initial Catalog=MyCatalog")
在第一行和第三行建立并初始化了一個ADODB連接配接對象。在第四行,就像我們前邊所說的,ADO連接配接對象封裝了兩種OLE DB對象:資料源和對話。在連接配接字元串中,你将看到一個之前沒有讨論過的屬性:“Initial Catalog”。它定義伺服器上将被這個連接配接使用的資料庫。讓我們使用這個連接配接對象來完成我們之前列舉的資料挖掘任務。
對于中繼資料發現(Discovery),ADO連接配接提供了一個OpenSchema函數(在OLE DB對話中打包了IDBSchemaRowset接口)。
OpenSchema 包含3個參數:
· 枚舉,用來描述将要被發現的計劃。
· 限制集,将被應用于發現操作的一系列限制。
· 全局唯一辨別符(GUID),描述提供程式指定的計劃。
我們嘗試在MyCatalog 資料庫上發現伺服器端的挖掘模型。這不是資料挖掘模型計劃(指定的提供程式計劃)中預先計劃好的枚舉,是以第一個參數就是adSchemaProviderSpecific。第二個參數,限制集,包含用來查找模型(“MyCatalog”)的目錄名。第三個參數包含Analysis Services OLE DB提供程式中,能識别挖掘模型計劃的GUID的字元表。
6 Const DMSCHEMA_MINING_MODELS="{3add8a77-d8b9-11d2-8d2a-00e029154fde}"
7 Dim Restrictions()
8 Restrictions = Array("MyCatalog")
9 Dim rsSchema As ADODB.Recordset
10 Set rsSchema = Conn.OpenSchema(
adSchemaProviderSpecific,
Restrictions,
DMSCHEMA_MINING_MODELS)
資料挖掘的OLE DB規範包含分析服務的OLE DB提供程式支援的計劃的完整定義,包括發現(Discovery)語句傳回的列和限制。
OpenSchema傳回一個ADODB.Recordset對象。Recordset對象封裝了一個表型伺服器響應。下面我們将看到如何周遊這個對象以及如何從中提取資訊。這段代碼的目的是枚舉挖掘模型的名字。 像我們所知道的一樣,在資料挖掘OLE DB裡,挖掘模型計劃中的每一行對應一種挖掘模型,并且包含一個“MODEL_NAME”列,列中存儲挖掘模型的名字。
下面一段代碼顯示如何從Recordset 對象中查找指定的列,以及如何從這些列中提取資訊。
11 ' 從(MODEL_NAME)中查找列
12 Dim iModelNameColumn As Integer
13 For iModelNameColumn = 0 To rsSchema.Fields.Count - 1
14 If rsSchema.Fields(iModelNameColumn).Name = "MODEL_NAME" Then
15 GoTo Found
16 End If
17 Next
18 Error (1)
19 Found:
20 ' 讀取Recordset的傳回值
21 rsSchema.MoveFirst
22 While Not rsSchema.EOF
23 Debug.Print rsSchema.Fields(iModelNameColumn).Value
24 rsSchema.MoveNext
25 Wend
如你所看到的一樣,這段代碼先周遊了Recordset 對象的所有字段。每個字段表示響應的一列。我們根據這些字段的索引MODEL_NAME進行查找。如果找到了這樣的一列,就開始周遊它的行内容,否則就報錯。
為了周遊行, Recordset指針先移到資料的開始位置。之後一行一行的讀取行資料。對于每一行都讀取和使用MODEL_NAME字段的值。
一些資料挖掘任務(如建立新中繼資料對象或訓練已存在的對象)可以通過發送DDL語句到伺服器來執行。讓我們看看DDL語句是如何被OLE DB通過ADO來傳輸的。我們将使用與之前一樣的連接配接對象,并介紹一個新的OLE DB對象的ADO包, ADODB.Command對象:
26 Dim Cmd As ADODB.Command
27 Set Cmd = New ADODB.Command
28 Cmd.ActiveConnection = Conn
29
30 Dim strProcessDDLStmt As String
31 strProcessDDLStmt = "" _
32 & " </Process
xmlns=""http://schemas.microsoft.com/analysisservices/2003/engine"">" _
33 & " <Type>ProcessStructure</Type>" _
34 & " <Object>" _
35 & " <DatabaseID>CheckInTestDB</DatabaseID>" _
36 & " </Object>Structure1" _
37 & " </Process>" _
38 & " "
39
40 Cmd.CommandText = strProcessDDLStmt
41 Cmd.Execute
42
指令在一個活躍連接配接中執行。這個活躍的連接配接就是第28行中的内容。一般來說,這個指令包含着執行語句。這在CommandText 屬性(第40行)中設定。當ADO為Analysis Services 2005打包OLE DB提供程式時,CommandText屬性支援DMX語句和DDL語句(就像之前所展示的一樣)。指令的執行被“執行(Execute)”方法初始化(第41行)。執行經常傳回一個ADODB.Recordset 對象(可用于之前的發現(Discovery)代碼段的表型伺服器響應)。但是,處理操作是沒有伺服器響應的,它不傳回成功或失敗。如果出現了一個錯誤,ADO将報一個異常并停止正在運作的代碼。是以,如果執行到了第42行,就說明執行成功了。
在之前的代碼中,可以将CommandText 屬性改為一個DDL語句,比如Alter、Create或者Drop;或改為一個DMX語句,如CREATE MINING MODEL或者INSERT INTO,它可以實作絕大多數挖掘任務。唯一一個需要附加補充的是查詢挖掘模型。DMX查詢與一般的無響應資訊語句不同,因為:
· 它傳回一個表型結果。
· 表型結果可能包含多級的表(嵌套表)。
· DMX 支援參數。
我們可以從傳回單級表的DMX查詢開始看起,使用我們最開始使用的對象來對比(指令和連接配接):
43 Cmd.ActiveConnection = Conn
44 Cmd.CommandText = "SELECT NODE_CAPTION FROM DecisionTree1.CONTENT" _
45 &"where NODE_TYPE=2"
46
47 Dim rs As ADODB.Recordset
48 Set rs = Cmd.Execute()
49 rs.MoveFirst
50 While Not rs.EOF
51 Debug.Print rs.Fields(0).Value
52 Wend
53 rs.Close
如我們之前所提到的一樣,DMX語句由CommandText屬性來傳輸。與發現(Discovery)使用相同的方式來周遊Recordset。第44行用到的查詢隻傳回一列,這裡并不真正需要從recordset 字段來查找列;相反的,它應該由它的索引得到(第51行)第0字段的内容。也請注意第53行中對Recordset 的配置。當Recordset 是活躍的時候,指令對象不能執行之後的語句。
OLE DB規範也允許傳回更加複雜的結果集,如表型列或者嵌套表。下面給出一個伺服器響應是嵌套表的例子,我們将使用DMX語句來添加查詢更複雜的第二列NODE_DISTRIBUTION。是以新的查詢如下所示:
"SELECT NODE_CAPTION, NODE_DISTRIBUTION FROM DecisionTree1.CONTENT WHERE NODE_TYPE=2"
NODE_DISTRIBUTION 是一個典型的嵌套表例子。根據資料挖掘的OLE DB規範,模型中的NODE_DISTRIBUTION列包含目前行所在節點的屬性值的分布。例如,在一棵預測頭發顔色的決策樹中,對每一個樹節點,這一列都描述有多少執行個體是黑發,多少執行個體是金發,多少執行個體是灰發。
執行指令在新的一列中并不改變。實際上唯一需要改變的就是Recordset的周遊代碼,需要進行适合表的新列的改變。在代碼中可以很容易的從Recordset 作為字段值的屬性傳回一列的值。如果執行個體中的列是一個嵌套表,列值将産生一個新的Recordset,再周遊整個嵌套表。
是以,在51行以下應該執行如下的代碼:
52 Debug.Assert( rs.Fields(1).Type = adChapter)
53 Dim nestedRS As ADODB.Recordset
54 Set nestedRS = rs.Fields(1)
55 nestedRS.MoveFirst
56 While Not nestedRS.EOF
57 Debug.Print nestedRS.Fields(0).Value
58 Wend
59 nestedRS.Close
第52行語句用來在将值寫入嵌套Recordset前,确認列的類型是正确的。這行語句也可以用來判斷制定的列是不是一個嵌套表。
應為擁有周遊嵌套表的能力,此時資料挖掘查詢産生的任何傳回值都可以在你的應用中被使用了。
DMX也支援查詢中的參數。參數可以取代DMX查詢中的任何值。例如,可以使用一個參數來代替上述查詢語句中where子句裡的NODE_TYPE 的值“2”。在資料挖掘應用中,也有少數參數非常有用的場景,比如生成一個單獨的查詢(關于單獨查詢的詳細内容,請看“Adomd.NET”一節)。
想在DMX查詢中将一個值變為一個參數,我們使用參數訓示标志@來開始替換,使用“@唯一的參數名”。這樣VBA代碼片段中的第45行就可以變成:
45 &"where NODE_TYPE=@typeParam"
然後,在執行指令之前,我們應該插入這樣的一段代碼來確定能正确的使用新的參數。請注意,盡管實際資料可能與參數的值很不同,但是使用參數并不會改變伺服器響應的格式。是以上述周遊Recordset 的代碼可以并不改變。
46 Cmd.NamedParameters = True
47
48 Dim typeParameter As ADODB.Parameter
49 Set typeParameter = Cmd.CreateParameter()
50 typeParameter.Direction = adParamInput
51 typeParameter.Name = "typeParam"
52 typeParameter.Value = 2
53 typeParameter.Type = adInteger
54 typeParameter.Size = 4
55
56 Cmd.Parameters.Append typeParameter
在分析服務的OLE DB提供程式中使用參數時,如下的幾步非常重要:
· 指令必須使NamedParameters可用(第46行)。分析服務的OLE DB提供程式隻支援命名了的參數。
· 參數的名字必須符合查詢中的相應參數,但是在這裡不使用@字首(第51行)。
· 可以隻傳輸參數(第50行)。
· 參數的類型和大小必須聲明(第53和54行)。
想對資料挖掘的OLE DB規則進行很好的了解,需要充分使用Microsoft Analysis Services 2005資料挖掘的特性。一旦設計好了資料挖掘查詢,OLE DB就成為了一個可以執行它們、可以管理資料挖掘伺服器的完整且具有通用性的API。關于在非托管C++或ADO.NET中C#使用OLE DB 的代碼,請看本文附錄2中的代碼片段。
Analysis Management Objects – AMO
如同名字所顯示的一樣,AMO是一個用來管理任務的API。它非常适用于描述中繼資料屬性的細節資訊。AMO是一種非常符合微軟分析服務的資料定義語言(DDL)的對象模型,在AMO中描述中繼資料對象。它允許疊代中繼資料對象、建立新對象、修改已存在對象。在AMO對象模型中,用DDL描述的每個屬性都可以被檢查和修改。AMO也可以用來檢查和修改伺服器屬性,包括注冊/非注冊型插件算法,或者可注冊/不可注冊資料挖掘算法。AMO在中繼資料的定義和發現,以及伺服器對象的訓練上都非常有用。但是,它并不支援執行查詢語句。
在讨論細節資訊之前,我們應該先來說明一下AMO是一個托管庫。是以,它可以在由通用執行時間元件(CLR——Common Language Runtime)相容的程式設計語言開發的應用程式中使用,如C#、托管執行的C++、或者Visual Basic .NET。要想使用AMO, 我們需要在客戶機上安裝SQL Server 2005連接配接元件。
在使用AMO的時候,我們首先應該在應用程式的開始處引用Microsoft.AnalysisServices.dll 庫。之後,AMO對象模型就可以使用了。先來連接配接伺服器:
Microsoft.AnalysisServices.Server server = new Server();
Server.Connect("localhost");
一旦連接配接成功,伺服器端的中繼資料對象就可以被分級檢查到:
Databases dbCollection = server.Databases;
foreach( Database db in dbCollection )
{
MiningStructures structCollection = db.MiningStructures;
foreach( MiningStructure struct in structCollection)
{
Console.WriteLine( struct.Name );
}
}
如果想修改已存在的中繼資料對象,我們隻需要修改它的屬性。使用如下代碼:
model.Algorithm="Microsoft_Decision_Trees".
then call "Update".
model.Update();
當Update被調用時,你所做的修改就将被送出到伺服器,同時重新整理本地的資訊集。
在伺服器上添加一個新的中繼資料、在各自的集合上建立新成員這兩個方法,與修改功能也是非常相似的:
MiningStructure myStructure;
MiningModel myModel = myStructure.Models.Add();
之後填充對象的内容:
myModel.Name = "New Model"
myModel.Algorithm = "Microsoft_Clustering"
然後通過調用Update()來送出對伺服器端所作的修改:
myModel.Update();
當操作未能成功執行完畢時,更新操作(Update)将傳回一個異常。通常産生異常的情況是:
· 被更新的屬性不完整或者不一緻。
· 由于目前使用者未能擁有足夠的權限而引起的伺服器不能更新對象。
當出現異常的時候,我們可以找到問題的所在并修複它。
AMO在處理Analysis Services 2005伺服器端中繼資料上具有很強的功能,但是它并不支援之前所提到的資料挖掘任務中的部分任務。它不提供進度通知的功能,也不提供查詢挖掘模型的功能(一般情況下,它不支援DMX語句的執行)。如果你的應用需要浏覽和檢查Analysis Services 2005伺服器,使用AMO是最好的選擇。但是,如果除此以外你還需要執行語句或者顯示進度通知,AMO的功能就不夠了。下一章将為大家介紹兩種通用API,它們支援分析服務所公開的所有特性。
進度通知:使用跟蹤對象
當在伺服器端同時執行多個事件時,微軟分析服務就會發送一系列通知。這些事件包括使用者的登入登出、 執行請求的開始位置和結束位置、不同伺服器對象正在執行的進度情況。在接收到通知以後,管理者就可以檢查伺服器在每一刻的狀态。正在處理伺服器對象的使用者也可以從中了解正在執行的操作是什麼,以及還需要執行多長時間。
這一節中,我們将讨論應用程式是如何從Microsoft Analysis Services 2005伺服器來接收進度通知的。我們從如何發現伺服器發出的通知看起。 之後,我們簡短的介紹應用程式如何訂閱它所感興趣的伺服器通知。最後,我們給出一個需要處理進度通知的應用程式的程式設計模型。
在進行深入的讨論之前,我們先來明确一下,SQL Server 2005使用一個用戶端分析器(Profiler)來處理這些通知。使用這個分析器,使用者可以真正根據自己的需要進行通知的選擇,然後檢查這些通知的具體内容。分析器應用程式也允許記錄伺服器通知,可以為了以後檢查通知内容而先将它們儲存下來。它也可以使用性能計數器(Performance Counter)将正在執行的不同的性能訓示器的相關伺服器通知整合起來。大多數應用程式并不需要在代碼中執行伺服器通知,這一節針對那些需要使用資料挖掘用戶端進階使用者接口的開發者來進行講解。
伺服器發出的每種通知都是一個表型行。這一行包含一些基本資訊,例如通知的種類、時間戳、伺服器程序ID、以及伺服器的名稱。其它列分别對應特定的事件,例如處理通知的Progress Total、起始時間、結束時間、在伺服器上已經執行的時間。伺服器發出的所有通知被存放在一張虛拟表中,它為每個事件保留一行,以及被任何伺服器事件支援的所有列的集合。這意味着:如果列A隻被一個指定事件支援,它将在這張虛拟表中所有的事件行中出現,但是不支援A的事件的列A字段是空值。說這張表是一張虛拟表,是因為它并不存放在記憶體中,也不能被直接通路到。當沒有人去看伺服器事件進展情況的時候,它被存放在一個隻有管理者可以通路到的“flight-recorder”檔案中,用來提供過去出現錯誤的原因相關的有價值的資訊。
這張虛拟表中的列集可以用正常XMLA發現語句(Discovery)來發現。這個計劃的XMLA名稱是DISCOVER_TRACE_COLUMNS,它的GUID是{a07ccd18-8148-11d0-87bb-00c04fc33942}。這個計劃中的每一行描述事件表中的一列,計劃至少有一列,使用事件的XML格式進行描述。XML格式的描述如下所示:
<COLUMN>
<ID>0</ID>
<TYPE>1</TYPE>
<NAME>EventClass</NAME>
<DESCRIPTION>Event Class is used to categorize events.</DESCRIPTION>
<FILTERABLE>false</FILTERABLE>
<REPEATABLE>false</REPEATABLE>
<REPEATEDBASE>false</REPEATEDBASE>
</COLUMN>
就這本白皮書來說,我們所感興趣的屬性有 ID、 名字(Name)和描述(Description)。
ID 是列的數值型标志符。像我們關于事件的讨論一樣,一個事件通過定義ID來指定列。列的名字和描述對于用戶端來說非常重要。基于這些屬性,用戶端應用程式可以判斷哪些列是自己所感興趣的列,哪些列是可以忽略的。ID為0的列是“伺服器通知”這張虛拟表中最重要的列,因為它是用來定義事件類型的。Microsoft Analysis Services 2005釋出的所有通知都包含此列。
發現伺服器釋出的事件是一個與發現列很相似的任務。使用的XMLA計劃是 DISCOVER_TRACE_EVENT_CATEGORIES,它的GUID是 {a07ccd19-8148-11d0-87bb-00c04fc33942}。這個任務中的每一行描述一個伺服器釋出的事件類别,任務記錄事件類别中的全部事件。當使用XML形式定義事件類别時,任務中的代碼如下所示:
<EVENTCATEGORY>
<NAME>Queries Events</NAME>
<TYPE>0</TYPE>
<DESCRITION>Collection of events for queries.</DESCRITION>
<EVENTLIST>
<EVENT>
<ID>9</ID>
<NAME>Query Begin</NAME>
<DESCRIPTION>Query begin.</DESCRIPTION>
<EVENTCOLUMNLIST>
<EVENTCOLUMN>
<ID>0</ID>
</EVENTCOLUMN>
<EVENTCOLUMN>
<ID>2<ID>
</EVENTCOLUMN>
...
</EVENTCOLUMNLIST>
</EVENT>
<EVENT>
<ID>10</ID>
<NAME>Query End</NAME>
<DESCRIPTION>Query end.</DESCRIPTION>
<EVENTCOLUMNLIST>
<EVENTCOLUMN>
<ID>0</ID>
</EVENTCOLUMN>
<EVENTCOLUMN>
<ID>2</ID>
</EVENTCOLUMN>
...
</EVENTCOLUMNLIST>
</EVENT>
....
</EVENTLIST>
</EVENTCATEGORY>
是以,一個事件類别可以包含多個帶不同ID的事件。如之前所說的一樣,每個事件擁有它們自己的列,并且它們都包含提供事件類資訊的列0。
另一個特殊的列是列1,事件子類(event subclass)。它被多個事件共享。事件子類這一列允許跟蹤使用者,例如,我們可以用它來區一個ProgressStart事件到底是來自于資料挖掘,還是OLAP次元處理。如果一個事件中出現了列1,這一列要比其它列的定義複雜一些。如下文所示:
<EVENTCOLUMN>
<ID>1</ID>
<EVENTCOLUMNSUBCLASSLIST>
<EVENTCOLUMNSUBCLASS>
<ID>1</ID>
<NAME>Process</NAME>
</EVENTCOLUMNSUBCLASS>
<EVENTCOLUMNSUBCLASS>
<ID>2</ID>
<NAME>Merge</NAME>
</EVENTCOLUMNSUBCLASS>
...
</EVENTCOLUMNSUBCLASSLIST>
</EVENTCOLUMN>
這個更加複雜的定義描述了目前事件中列1可能出現的所有值的含義。
當一個應用程式決定了它感興趣的事件或者事件列是哪些的時候,它可以訂閱這些資訊作為伺服器通知。這個訂閱功能在伺服器建立跟蹤對象的時候被建立。跟蹤對象的功能與包含伺服器通知的虛拟表的視圖的功能相似。除此功能以外,跟蹤對象存有伺服器對象的所有屬性。它可以被建立、修改、删除以及根據許可情況進行限制。一個跟蹤聲明會指定它感興趣的事件(類似于關系型視圖中的WHERE子句)以及事件将傳回哪些列。定義跟蹤的DDL可以包含更多的過濾條件,如“隻傳回那些列C是特定值的傳回值”,但是這些過濾條件并不是本文要讨論的内容。這些進階選項可以從SQL Server 2005 線上文檔中進行了解,在文檔中有專門一節“跟蹤要素(分析服務腳本語言)”。
讓我們來考慮這樣的一個應用程式,它用來訓練挖掘模型,而且隻訂閱了如下進度報告事件:
· Progress Report Begin (事件ID是5)
· Progress Report Current (事件ID是7)
· Progress Report End (事件ID是6)
· Progress Report Error (事件ID是8)
對每一個事件來說,感興趣的列是:
· 列 0, EventClass。
· 列9, ProgressTotal,完成目前任務所需要執行的步驟數(此列僅在Progress Report Current事件可用)。
· 列10, IntegerData,資料挖掘進度通知中的屬性,存儲正在執行的任務的目前步驟(此列僅在Progress Report Current事件可用)。
· 列42, TextData,包含目前步驟的較長的描述。
我們使用下面的DDL語句建立這個跟蹤:
<Create
xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
<ObjectDefinition>
<Trace>
<ID>DemoTrace</ID>
<Name>DemoTrace</Name>
<Events>
<Event>
<EventID>5</EventID>
<Columns>
<ColumnID>0</ColumnID>
<ColumnID>42</ColumnID>
</Columns>
</Event>
<Event>
<Event ID>6</Event ID>
<Columns>
<ColumnID>0</ColumnID>
<ColumnID>42</ColumnID>
</Columns>
</Event>
<Event>
<EventID>7</EventID>
<Columns>
<ColumnID>0</ColumnID>
<ColumnID>9</ColumnID>
<ColumnID>10</ColumnID>
<ColumnID>42</ColumnID>
</Columns>
</Event>
<Event>
<EventID>8</EventID>
<Columns>
<Columns>0</Columns>
<Columns>42</Columns>
</Columns>
</Event>
</Events>
</Trace>
</ObjectDefinition>
</Create>
一旦跟蹤被建立,用戶端應用程式就将訂閱這個跟蹤。訂閱跟蹤的功能與執行傳回表型結果的伺服器請求的功能非常相似。它将傳回一個表,表中包含由select事件選出的列。與普通的伺服器查詢相比,訂閱(Subscribe)語句将一行行的儲存傳回的事件,直到發送一個停止訂閱(Unsubscribe)指令給伺服器,訂閱語句才終止執行。進度通知僅在操作未完成時有用;在所有的操作都執行完畢後,進度通知就沒有什麼用處了。這也就是為什麼帶進度通知的模型使用兩個不同線程進行處理的原因。
第一個線程建立跟蹤,然後通知第二個線程進行訂閱。
第二個線程進行訂閱,并确認它設定了所有通路響應資訊表而需要的執行屬性。隻要有伺服器端響應出現,它馬上能一行一行的對表進行通路,而不需要等所有響應都發送回來後再進行操作。
之後,第一個線程發出處理指令,并等待處理完成。
同時,第二個線程收到進度通知,并在使用者界面顯示它們。
當處理操作進行完畢,不論成功還是失敗,第一個線程必須停止訂閱跟蹤并将跟蹤删除。
在删除跟蹤之前,第二個程序将接收到表結束的通知,第二個程序結束。
在之前的章節中,我們已經知道了如何在OLE DB和Adomd.NET中送出語句。為了在傳輸結束前得到所需的行,即得到結束響應,執行指令應該接收一個特定的API标志。例如,在OLE DB中,DBPROP_CANFETCHBACKWARDS 和DBPROP_CANSCROLLBACKWARDS屬性必須為否(FALSE)。在Adomd.NET中,ExecuteReader 函數有一個可選屬性CommandBehavior,它應該被設定為隻進(Forward only)執行模型SequentialAccess。
下面的代碼是包含第二個線程的函數以及主線程的程式架構:
AdomdConnection conn = new AdomdConnection();
conn.ConnectionString = "Data Source=localhost; " +
"Initial Catalog=MyCatalog";
conn.Open();
AdomdCommand cmd = new AdomdCommand();
cmd.Connection = conn;
cmd.CommandText = // 将建立跟蹤的代碼拷到這裡!!
// 執行建立查詢的語句
cmd.ExecuteNonQuery();
// 啟動一個新的線程來讀取跟蹤
Thread traceThread = new Thread(new ThreadStart(TraceProc));
traceThread.Start();
// 繼續進行處理操作
cmd.CommandText = // Processing statement,DDL or DMX, here
// 執行處理指令
try{
cmd.ExecuteNonQuery();
}
catch (System.Exception ex){
// 報錯
Console.WriteLine(ex.Message);
}
finally{
// 不論處理結果是什麼都删除跟蹤
cmd.CommandText =
"
"xmlns=/"http://schemas.microsoft.com/analysisservices/2003/engine/">"+
" " +
" DemoTrace" +
" " +
"";
cmd.ExecuteNonQuery();
}
// 跟蹤讀取線程進度
public static void TraceProc()
{
// 在相同伺服器上開始一個新的聯接AdomdConnection
AdomdConnection conn = new AdomdConnection ();
conn.ConnectionString = "Data Source=localhost; " +
"Initial Catalog=MyCatalog";
conn.Open();
AdomdCommand traceCmd = new AdomdCommand();
traceCmd.Connection = conn;
// DDL訂閱(Subscribe)指令
traceCmd.CommandText = "<Subscribe "+
"xmlns=/"http://schemas.microsoft.com/analysisservices/2003/engine/">"+
" <Object>" +
" <TraceID>DemoTrace</TraceID>" +
" </Object>" +
"</Subscribe>";
// 得到一個前向Data Reader
AdomdDataReader traceRdr =
traceCmd.ExecuteReader(CommandBehavior.SequentialAccess);
try{
// 從跟蹤程式中讀取進度通知并顯示
while (traceRdr.Read()){
string textData = traceRdr.GetString(3);
if (textData != null)
Console.WriteLine(textData);
}
}
catch(System.Exception {
// 跟蹤的異常情況:有可能是“伺服器上的跟蹤已經被删除(The trace was deleted
// on the server)”終止了跟蹤
}
finally{
// 關閉使用者界面(UI)
Console.WriteLine("Trace Completed");
}
}
向我們之前所說到一樣,SQL Server 的分析器使用起來非常容易,建立跟蹤也很容易。它提供了很多跟蹤對象的進階功能,如行過濾器。
無伺服器的資料挖掘:本地挖掘模型
Microsoft Analysis Services 2005 為程序内伺服器提供有限的功能。它提供了一種可以将伺服器代碼裝載到你的應用程式的記憶體空間中的方法,而不用真正打開一個到伺服器的連接配接。這個功能可以在面向資料挖掘的檔案“本地挖掘模型”或者面向OLAP的檔案“本地立方體”中查詢。在本文中,我們使用“本地伺服器”。
本地伺服器的目的是允許在脫機情況下進行與資料挖掘有關的操作。要用到這一特性的場景多為(但不限制于)嵌入式應用。
例如,我們可以在真正的伺服器上設計并訓練一個非常有用的挖掘模型,這個模型可以在叢集上區分WEB請求,例如“小負荷的快速請求”、“小負荷的慢速請求”、“大負荷的慢速請求”。這樣的一個挖掘模型可以用來解決基于叢集的不同WEB伺服器的平衡裝載問題。但是,平衡裝載解決方案也可能在分析伺服器上執行請求,用來判斷發送請求的叢集是哪個,哪台機器存在潛在的時間消耗問題和可能出現安全問題(分析服務所在的機器應該在平衡裝載方案所在的網絡内,隻不過它一般在防火牆以外)。而程序内伺服器就是此種情況的解決方案。一旦訓練模型從伺服器上傳輸到具有裝載平衡解決方案的機器上,這個模型就可以使用程序内伺服器來執行請求。此時,請求的速度非常快(這些請求在同一個程序中,遠強于通過網絡進行傳輸),微軟分析服務也是安全的被保護在防火牆内的。
微軟分析服務儲存所有的被調用的資料檔案夾中的中繼資料(帶挖掘結構和挖掘模型的資料庫),這個檔案夾是在運作安裝程式時進行配置的。本地伺服器在一個檔案中儲存所有的中繼資料,這個檔案一般都使用擴充名.cub。一旦連接配接到本地伺服器,應用程式就可以開始建立資料庫、挖掘結構、挖掘模型了,并可以像在真正的伺服器上一樣使用它們。所有的中繼資料都将被建立和存儲到.cub 檔案中。如果應用程式被關閉了,當本地伺服器被舊的.cub 檔案重新初始化的時候,所有的已存在中繼資料将被儲存和加載。
所有之前所提到的資料通路API都可以連接配接到本地伺服器。開發者隻需要使用.cub 檔案的名字來代替之前的連接配接字元串中的伺服器名(在代碼示例中是“localhost”)即可。下面,給出一個Adomd.NET的例子:
AdomdConnection conn = new AdomdConnection();
conn.ConnectionString = "Data Source=c://test.cub; ";
conn.Open();
Similar code for AMO looks like:
Microsoft.AnalysisServices.Server server = new Server();
server.Connect("c://test.cub");
我們需要記住的是,在執行建立之前,.cub 檔案中并沒有資料庫。
是以,我們需要做的第一件事就是建立一個新的資料庫。需要這樣做的一個例外是DMX的CREATE語句。當使用CREATE語句在沒有可用資料庫的情況下建立新的挖掘模型或者新的挖掘結構時,.cub 檔案中将自動生成一個新的目錄。
在連接配接成功以後,本地伺服器就可以像真正的伺服器一樣被通路了。但是,本地伺服器所能提供的連接配接也具有一些局限性:
· 本地伺服器不支援多個同步連接配接(是以,像在之前一節讨論的一樣,我們無法收到進度通知)
· 本地伺服器不支援資料庫備份和存儲。
· 本地伺服器不支援DMX 導入(IMPORT)語句。但是它支援DMX導出(EXPORT)語句。
· 本地伺服器隻支援有限的資料挖掘算法(Microsoft_Decision_Trees 和 Microsoft_Clustering).
擴充SQL Server 資料挖掘功能
到目前為止,我們已經讨論了很多種資料通路API在伺服器上執行語句的方法。無論是資料定義語言(DDL)還是DMX,這些語句都是資料挖掘的類SQL語言。在這一節中,我們将讨論擴充伺服器功能的若幹方法:
· 添加新的函數和存儲過程。
· 添加新的伺服器端算法。
· 添加新的模型檢視器(用戶端)。
使用Adomd.NET Server 擴充DMX
Adomd.NET伺服器是一個托管庫,它和Microsoft Analysis Services 2005伺服器産品一起進行安裝。是以,它可以被任何托管語言所使用(如C# 或Visual Basic .NET)。Adomd.NET伺服器可以用來建立兩類伺服器擴充:函數和存儲過程。
存儲過程是一個單機執行單元。它可以通過DMX語句被自己調用,例如:
CALL TestAssembly.MyNamespace.MyClass.MyStoredProcedure()
存儲過程可以像任何DMX請求一樣傳回表型傳回值;或者隻執行伺服器端操作,傳回成功或失敗。
伺服器函數是一個輔助執行單元,它參與正常DMX查詢。查詢可以是這樣的:
SELECT Function1() FROM MyModel WHERE Function2()
在Microsoft Analysis Services 2005中,為了建立一個函數或者存儲過程,我們需要建立一個托管類庫(或程式集),然後在伺服器上部署它。程式集可以讓所有伺服器可視(需要伺服器管理許可),或者僅存在于資料庫中(需要資料庫管理許可)。程式集對象有一些安全特性,包括許可集(描述授權給.NET代碼的許可)和模拟資訊(當程式集中的代碼被執行時,什麼帳号将被模拟)。SQL Server 2005線上文檔包含關于程式集的許可集以及模拟模式的細節資訊。就本文的來說,我們将使用Safe許可集,并模拟目前使用者。程式集中公共類的每個公共方法都可以用作伺服器函數或者存儲過程。它們之間的差別在于伺服器函數應該有傳回值(一個數值或者一個表型目錄),而存儲過程傳回一個空值(表示執行成功)或者一個表型目錄(DataTable對象或者被IDataReader接口執行的對象),存儲過程不能傳回一個數值(實際上它們可以傳回數值,但是數值不能被調用者當作CALL DMX語句的傳回值而傳回)。
當引用托管存儲過程或者伺服器函數的時候,必須使用完整的限定名。完整的限定名的BNF格式如下所示:
<AssemblyName>[.<Namespace>]+[.<ClassName>].<MethodName>
AssemblyName 是程式集的名字,它在程式集被部署的時候定義。它是必須被提供的。<Namespace> 是簡單(或嵌套)命名空間,包含目标類和方法。<ClassName> 是包含方法的類。<MethodName>是真正的方法。<MethodName> 也是必須被提供的。請注意<Namespace> 和 <ClassName> 并不是強制性必須提供的。當在一個程式集中的多個函數/存儲過程互相之間不明确時,必須使用這兩個字段。完整的限定名(包括Namespace和ClassName)可以被本章内所有的語句使用。
當建立伺服器函數或者存儲過程時,時刻注意這些對象可能被多個使用者同時調用是非常重要的。 類執行個體不能被多個調用所共享。是以當一個存儲過程或者函數被調用的時候,沒有一個動作來改變類的狀态。而是根據傳輸給方法的參數來重新初始化狀态的。說到參數:它們隻能是數值。伺服器函數和存儲過程不能使用表作為參數。
下面我們使用一個DMX查詢例子來示範一個簡單的伺服器函數是如何豐富使用者經驗的。這個查詢在挖掘模型Model1中使用,這個模型用來根據性别列的F值和M值預測性别。我們嘗試将這個查詢進行改進:
SELECT Gender FROM Model1 [PREDICTION JOIN ... AS T]
這個查詢将傳回一列是M或F的值。
下面的伺服器函數包含的代碼将性别訓示器轉換為更加可讀的字元串。它将M傳回為Male ,将F傳回為Female 。
namespace TestSP
{
public class StoredProc
{
public string RecodeGender(string strGender)
{
if (strGender == "M") return "Male";
if (strGender == "F") return "Female";
return strGender;
}
}
}
一旦類庫包含了已經建立和部署了的類定義,伺服器函數可以這樣被調用:
SELECT TestSP.TestSP.StoredProc.RecodeGender(Gender) FROM Model1PREDICTION JOIN ... AS T
新的查詢将傳回包含Male 或 Female的列。
讓我們現在來關注一下伺服器函數的參數:列名。DMX分析引擎檢測到Gender 是一個列名,它是一個可預知的列,這一列在伺服器函數調用的時候被指派。我們也可以使用PREDICTION JOIN 操作(類似于T.Gender)的輸入資料中來傳輸列,用以代替這個可預知的列。這個函數可以被修改成多個參數,并且通過這些參數執行多個操作(比如串聯字元串、帶數字參數的算術運算符等等)。
簡單的存儲過程可以使用相同的方式來定義。在相同的類中,我們可以添加一個傳回表型目錄的新函數(在本例中是一個DataTable)。
public DataTable GetWeekDays()
{
DataTable tbl = new DataTable();
tbl.Columns.Add("Day", typeof(int));
tbl.Columns.Add("Name", typeof(string));
object[] row = new object[2];
row[0] = 0;
row[1] = "Sunday";
tbl.Rows.Add(row);
row[0] = 1;
row[1] = "Monday";
tbl.Rows.Add(row);
...
row[0] = 6;
row[1] = "Saturday";
tbl.Rows.Add(row);
return tbl;
}
存儲過程可以被DMX語句調用:
CALL TestSP.TestSP.StoredProc.GetWeekDays()
現在讓我們來看更加複雜的情況。使用[College Plans]挖掘模型,它根據父母的鼓勵和IQ來預測學生的大學計劃,我們需要将每個帶說明的預測結果聯系起來。我們可以使用之前的查詢來獲得預測結果。預測的原因可以在挖掘模型中每個節點的NODE_DESCRIPTION列中找到。預測節點可以通過通用DMX功能PredictNodeId 來判斷。是以,這個查詢可以修改成這樣:
SELECT CollegePlans, PredictNodeId(CollegePlans) FROM [College Plans]
[PREDICTION JOIN ...]
作為本節的補充内容,我們可以忽略請求中的PREDICTION JOIN部分,因為它與伺服器函數和存儲過程的設計無關。
PredictNodeId 傳回節點的唯一名字NODE_UNIQUE_NAME,這個名字決定了預測的内容。關于NODE_UNIQUE_NAME 的描述NODE_DESCRIPTION可以通過執行内容查詢來判斷。例如:
SELECT NODE_UNIQUE_NAME, NODE_DESCRIPTION FROM [College Plans].Content
現在,為了得到帶預測描述的預測結果,我們需要将第一個查詢的結果和使用了NODE_UNIQUE_NAME 的第二個查詢的結果連接配接起來,使用它們二者的查詢結果作為連接配接的關鍵字。但是,至少在目前版本中,DMX是不支援連接配接(JOIN)子句的。
此時,我們可以寫一個傳回節點描述的伺服器函數,使用節點唯一的名字作為參數。假設我們已經定義了這樣的一個函數,它的名字是GetNodeDescription,查詢就變成了這樣:
SELECT
CollegePlans,
TestSP.TestSP.StoredProc.GetNodeDescription(
PredictNodeId(CollegePlans) )
FROM [College Plans] PREDICTION JOIN ...
如我們之前所提到的一樣,DMX執行引擎将在調用伺服器函數之前執行PredictNodeId。是以,伺服器函數将接收到參數——節點唯一的名字,此時它唯一需要做的就是去通路伺服器目前的上下文、浏覽挖掘模型的内容、以及确定各個節點的節點描述。
這時Adomd.NET伺服器就顯得非常有用了。為使用Adomd.NET伺服器,類庫必須包含一個特殊程式集的引用,它将伺服器内部開放給存儲過程和函數。這個程式集是msmgdsrv.dll,它在Microsoft Analysis Services 2005 伺服器安裝目錄中。
在添加好引用以後,我們可以這樣使用它:
using Microsoft.AnalysisServices.AdomdServer;
在使用程式集後,伺服器上下文已經作為靜态Context類可用了。
此時,程式集中的每種方法都可以使用Context 來通路伺服器目前的上下文了。
使用Context類與在應用中使用動态Adomd.NET用戶端庫的 AdomdConnection 對象是一樣的。在關于Adomd.NET 的章節中,我們已經讨論了使用Adomd.NET的類AMO方式公開伺服器中繼資料的方法。在用戶端API,中繼資料被發現(Discovery)方法重新得到;于此同時,在伺服器端Adomd.NET庫,它們直接通路伺服器記憶體空間。
再回來看我們嘗試編寫的函數,Context 對象提供了所有我們需要的内容,如下:
public string GetNodeDescription(string nodeUniqueName)
{
MiningContentNode node =
Context.CurrentMiningModel.GetNodeFromUniqueName(nodeUniqueName);
return node.Description;
}
這時發生了什麼:
· Context “知道”哪個是CurrentMiningModel,因為函數調用發生在DMX語句執行的時候,這個DMX語句擁有目前的挖掘模型(在我們的例子中是[College Plans]模型)。
· MiningModel 作為 CurrentMiningModel 公開的方法被傳回, GetNodeFromUniqueName 根據節點的唯一名稱傳回 MiningContentNode節點。
· MiningContentNode 有一個Description 屬性,這個屬性包含該節點的NODE_DESCRIPTION 值。
一旦程式集被部署完畢——被家長鼓勵且智商在110以上的學生,用伺服器函數進行查詢得到的結果是這樣的:
College Plans = Does not plan to attend
Node Description = "ParentEncouragement='Encouraged' and IQ >= 101"
我們的目标實作了;我們使用一個簡單的查詢得到了之前需要使用連接配接(JOIN)才能得到的結果, Analysis Services 2005并不支援JOIN。
Adomd.NET伺服器庫公開的Context 類包含所有關于發現(Discovery)的程式集,它使用用戶端AdomdConection對象。是以,在伺服器函數或者存儲過程中,開發者可以周遊一個或多個挖掘模型的内容,或者OLAP對象如立方體和次元。伺服器函數和存儲過程的一個非常重要的差別在于CurrentMiningModel 隻可以在伺服器函數中使用。這是因為DMX分析引擎不能通過如下的語句來判斷目前的挖掘模型:
CALL MyStoredProcedure()
即使存儲過程将挖掘模型名稱作為參數(一種常用的方法),也不存在簡單的描述目标挖掘模型将哪些參數作為考慮對象的方式。除此以外,存儲過程不能隻支援目前挖掘模型這樣的概念,因為有時候它不得不處理複雜的模型。
Microsoft Analysis Services 2005所提供的資料挖掘工具使用存儲過程來減少伺服器和用戶端間的總通訊量。大多數挖掘模型檢視器需要通過完全周遊來識别伺服器提供的模式。不使用存儲過程,整個挖掘模型不得不在用戶端被全部下載下傳,存儲在記憶體中,并且需要對其進行周遊才能知道哪些模式是所感興趣的。使用存儲過程,内容周遊在伺服器上進行(在伺服器上模型的内容已經裝載到記憶體中了),并且存儲過程的結果隻包括檢視器需要的資訊(例如在微軟叢集檢視器中檢視兩台機器的差別,或者在微軟相關規則檢視器中檢視含有指定内容的規則)。
與用戶端Adomd.NET 庫相似,在伺服器端,我們也定義一個AdomdCommand 對象。可以通過在目前資料庫上執行DMX查詢來使用這個對象。與用戶端指令的一個不同在于,伺服器端指令隻執行DMX語句(它不支援DDL或者OLAP MDX查詢)。伺服器端 AdomdCommand 與其用戶端指令非常相似:它提供ExecuteReader 和 ExeuteNonQuery 兩個方法,并且支援參數,也支援表型參數。
下面的代碼段向我們展示了一個伺服器端指令對象的例子,以及如何執行查詢的方法:
public IDataReader ExecuteContentQuery(string strModel)
{
AdomdCommand cmd = new AdomdCommand();
cmd.CommandText = "SELECT NODE_UNIQUE_NAME, NODE_RULE " +
"FROM [" + strModel + "].CONTENT";
return cmd.ExecuteReader();
}
這個機制也可以包含DXM查詢語句,修改後的語句如下:
CALL TestSP.testSP.StoredProc.ExecuteContentQuery( "College Plans" )
請注意,目前的伺服器并不支援傳回嵌套表的存儲過程。但是,存儲過程可以傳回表型結構(例如,DataTable 或者IDataReader對象),這些表型結構可以是伺服器端連續發出的普通表型查詢響應。這條規則有一個例外:存儲過程也可以傳回一個DataSet 對象。此時,DataSet 對象被完全串聯成一個字元串。不論DataSet 的結構是什麼樣子的,伺服器響應都包含一條線和一列值,列中存儲着DataSet 的串聯字元串。伺服器端負責存儲新DataSet 對象中的串聯字元串。用戶端 Adomd.NET API會自動進行存儲。
由伺服器端AdomdCommand 對象執行的DMX語句不一定非要被查詢。例如CREATE MINING MODEL 或者INSERT INTO語句都可以在這裡使用。如果一個挖掘模型在存儲過程中被建立或者删除,Context類公開的關系型資料模型集合就需要被更新(通過調用這些集合中的Refresh方法來實作)。
存儲過程和伺服器函數是DMX架構最有力的擴充。它們可以增強内容檢視器的性能,并且用一個全新的方式來展示訓練挖掘模型的一系列規則。并且,它們也增強了DMX查詢的功能和DMX語言的靈活性。
插件算法和内容檢視器
Microsoft Analysis Services 2005 提供一系列強大的算法,這些算法可以用來解決很大範圍内的商業問題。但是,一些特殊問題所需要的資料挖掘模型算法并不在常用的模型集合中。這也就是為什麼伺服器要提供插件控件來擴充算法的原因。新的算法必須是COM伺服器,它執行一系列指定的接口,并且使用另外一些由伺服器提供的接口。這樣的插件算法将自動使用新資料挖掘模型平台所提供的很多特性:
· 中繼資料定義和管理
· 粒度通路許可模型
· 具有高可擴充性的查詢
· 訓練和預測事例集标記
· DMX查詢語言及分析
· 對存儲過程和伺服器功能的支援
基本上,新的算法隻需要關心如何在訓練集中查找模式以及如何得到一個輸入事例,而不需要關心這兩個動作以外的事情。“SQL Server資料挖掘:插件算法”白皮書提供書寫插件算法的具體内容。
在用戶端,資料挖掘嘗試着提供了一些通用的算法檢視器。不管目前要處理的商業問題是什麼,這些檢視器都可以顯示每個算法有用的可視資訊。用來解決具體問題的資料挖掘應用程式則需要特定的檢視器,這些特定的檢視器可以更好的解決具體的問題。插件算法也可能并不公開被内置檢視器所管理的内容。這也就是為什麼資料挖掘工具提供了一種習慣模型檢視器插件的原因。關于這個插件的具體内容,請看《建立插件檢視器指南》。
應用場景建議
這一章試圖為開發者提供一些資料挖掘應用常見場景的快速開發建議。這些建議并不是全面的,而且有些建議可能具有一定的局限性。但是,它們是資料挖掘開發團隊在使用Microsoft Analysis Services 2005開發具體應用程式時的經驗之談。
先給出一個基本的建議:因為資料挖掘OLE DE規範包含DMX的所有内容,是以充分了解DMX是使用Microsoft Analysis Services的資料挖掘進行開發最快的方式。
應用場景1:簡單的資料挖掘預測
以下應用程式的目的是進行簡單的預測。
· 要編寫執行資料挖掘查詢操作(包含預測)的代碼,最簡單的方式是使用Adomd.NET。Adomd.NET一節包含的代碼可以直接複制到你的應用中,根據應用程式的具體情況修改後即可使用。
· 一個非常常見的場景是根據一個單一的執行個體進行預測(單一預測)。如果資料來自客戶的輸入,強烈建議你使用參數化語句,進而可以避免畸形DMX語句、使用者插入的DMX等語句。
· 如果應用程式進行預測以外的動作(允許客戶選擇模型、列之類的),使用Adomd.NET 公開的程式集是最簡單的發現伺服器中繼資料的方法。
應用場景2:Web應用——使用伺服器上現有模型的規則
除了上述場景外,WEB應用也應該可以将所有的使用者輸入作為查詢的參數進行傳輸。WEB應用除了有可能在執行DMX語句時報錯,也可能是被惡意使用者釋出的。這些惡意使用者最可能将一些DMX代碼加入到你的語句中。盡管DMX語句在設計時盡量注意縮小被感染後的危害(很多語句不許使用;SELECT語句不能随意的連接配接到DELETE 或DROP語句),惡意使用者仍然可以連接配接到鑽取(drill through)結果,或者至少可以産生一段畸形查詢,這段畸形查詢潛在的使你的應用變慢甚至摧毀你的應用。
WEB應用應該在明确定義的使用者帳号下運作。不允許公共WEB應用模拟遠端使用者。是以,我們應該小心的挑選運作這類應用的使用者,并且給這類賬号很小的權限。
當微軟分析伺服器持續運作的時候,WEB應用的性能将增強。這與其它請求建立新連接配接時的情況完全不同,它們可能引起連接配接池中的連接配接被終止。在執行其它請求的時候,想從連接配接池中傳回請求必須先确定該連接配接活躍可用。
Microsoft Analysis Services 2005提供一系列WEB控制(在進行安裝的時候可以友善的選擇它們的例子),它們可以使用豐富的圖表來示範挖掘模型的内容,類似于資料挖掘檢視器中的圖表。這些控制可以從圖表中看到。
對于聯合預測——常見的WEB資料挖掘應用類型——來說,DMX中新的文法要素明顯增強了性能(僅在Microsoft Analysis Services 2005中可用)。一個典型的例子是:Microsoft Analysis Service 2000中執行聯合預測使用TopCount 函數,用它來根據預測機率挑選預測的結果(例如,可能被目前使用者所購買的商品的清單)。而在Microsoft Analysis Service 2005中不再需要TopCount 。如下的查詢語句将更快的提供相同的結果:
SELECT Predict([Products], 5) FROM Model PREDICTION JOIN
應用場景3:為目前資料在伺服器上建立和訓練一個新的模型
在伺服器上部署一個資料模型需要擁有資料庫管理許可。挖掘模型會話不需要管理許可(如果伺服器管理者允許這個特性的話)。會話并不會使用無用的中繼資料來增加伺服器的負荷,是以在可能的情況下都可以使用會話。
如果訓練集是用戶端應用可用的,這裡有兩種訓練挖掘模型的方法:
· 關系型資料庫上的資料可以同時被伺服器端(讀)和用戶端應用(寫)看到。一旦資料進入到資料庫中,就執行指向剛進入資料庫的資料的綁定(DDL中的)來訓練模型,或者通過執行帶OPENQUERY 的INSERT INTO 語句來訓練模型,其中OPENQUERY 指向剛進入資料庫的那些資料。相比來說INSERT INTO語句更容易執行,但是它的性能比DDL綁定的處理性能要差些。 這是因為DDL綁定允許平行關系的查詢語句并聯處理模型的列集;而帶OPENQUERY的執行語句隻能處理一次,它将資料存儲到臨時存儲區後,一列列的進行處理。在資料集不是很大的時候,二者性能的差異并不明顯。
· 資料可以作為表型參數傳給INSERT INTO語句。Adomd.NET為此提供了強大的支援功能,資料可以由記憶體中的表中或者通過執行IDataReader 接口傳給INSERT INTO語句。此時,使用DDL綁定進行處理的性能優勢就失去了,但是語句更容易被執行了。有時,用戶端和伺服器端不在同一個網段内,這個網段内有它們都可以看到的那個關系型資料庫,這時候對資料庫的置入操作就無法完成。
小結
我們希望本文能涉及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的消息結構如圖2所示:
圖 2. MSAS2005消息格式
下面的例子包含一些常用的分析服務請求,使用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 ObjectExpansinotallow="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>";