将数据挖掘作为平台技术的思想,为“智能型应用程序”的发展提供了技术支持。智能型应用程序不需要定制代码来处理各种不同的环境,它直接从数据中学习商业规则。另外,因为商业规则会发生变化,所以需要通过重新处理代表业务逻辑的模型来自动更新智能型应用程序。智能型应用程序的示例有交叉销售应用程序(这些应用程序向用户提供相关的推荐信息)、呼叫中心应用程序(这些应用程序只显示有可能进行购买的某些客户)以及订单输入系统(这些系统在输入数据时,对该数据进行验证,无需任何自定义的代码)。这些只是冰山一角;SQL Server数据挖掘编程模型的灵活性和可扩展性将激发开发人员的创造力,从而使开发人员开发出更多类型的智能型应用程序。
在上一章中,介绍了Analysis Services的核心通信协议是XML for Analysis(XMLA)。该协议提供了高度灵活的、独立于平台的方法,该方法用于访问数据挖掘服务器。在客户端和服务器之间可以执行的所有操作都可以通过XMLA来执行。然而,虽然可以通过XMLA来执行所有这些操作,但是并不意味着必须通过XMLA来执行这些操作。
在本章中,将回顾编程的接口和对象模型,这些接口和对象模型使得编写使用Analysis Services的数据挖掘应用程序变得容易。本章将提供使用Visual Basic.NET来编写的示例,这些示例示范了如何为每个任务使用合适的接口来实现典型的数据挖掘任务。接着,将讨论SQL Server数据挖掘的一些特殊的功能,可以使用这些特性来最充分地进行数据挖掘编程。样例代码(包括使用Visual C#.NET编写的版本)都可以在wiley.com/tang上获得。
在本章中,将学习:
● API及其在数据挖掘上的应用
● 使用Analysis Services的API
● 使用AMO来创建和管理数据挖掘对象
● 通过ADOMD.NET来进行数据挖掘客户端编程
● 通过Server ADOMD.NET来编写服务器方的存储过程
14.1 数据挖掘API
在列出用于SQL Server数据挖掘的不同应用程序编程接口(Application Programming Interface,API)后,将看到一组只取首字母的缩写词。使人迷惑的是,大部分名称都不是根据它们的功能来确定的,而是与现有的技术有着非常紧密的关系。表14-1描述了在Analysis Services编程中使用的主要API以及这些API的描述信息。
表14-1 SQL Server挖掘API
|
API |
全 名 |
描 述 |
|
ADO |
ActiveX Data Objects |
提供从本地语言(例如Visual Basic)对数据对象(包括数据挖掘)的访问 |
|
ADOMD.NET |
ActiveX Data Objects(Multidimensional)for .NET |
提供从托管语言(例如Visual Basic.NET、C#和J#)对Analysis Services数据对象的访问 |
|
Server ADOMD |
Server ActiveX(r)Data Objects(Multidimensional) |
提供从运行在服务器内部的用户自定义的函数中对Analysis Services数据对象的访问 |
|
AMO |
Analysis Management Objects |
Analysis Services的托管接口,该接口提供一些对象来执行一些操作,例如,创建、处理等等 |
|
DSO |
Decision Support Objects |
来自SQL Server 2000的Analysis Services托管接口。保留该接口主要是为了支持向后兼容 |
|
DMX |
Data Mining Extensions |
对SQL进行扩展,从而支持数据挖掘的操作 |
|
OLE DB/DM |
Object Linking and Embedding for Databases/for Data Mining |
规范的名称,该规范定义了DMX语言并且引入了作为数据库对象的数据挖掘模型的概念 |
|
XMLA |
XML for Analysis |
与分析服务器进行通信的通信协议和XML格式,与任何平台无关 |
14.1.1 ADO
创建活动数据对象(ADO)的目的是帮助Visual Basic编程人员访问数据库中的数据。ADO库将OLE DB接口封装到对象中,从而可以更容易地对对象进行编程。因为OLE DB for Data Mining规范指明了数据挖掘提供程序首先是OLE DB提供程序,所以可以使用ADO来执行数据挖掘查询,正如使用ADO来执行关系数据库查询一样。
ADO将OLE DB接口的复杂性精简为三个基本的对象:连接、命令和记录集。连接对象用来连接服务器和发出模式行集查询。命令对象用来执行DMX语句,还可以检索该语句的执行结果。记录集对象包含了任何用来返回数据的查询的结果。
14.1.2 ADO.NET
ADO.NET是受托管的数据访问层。创建它的目的是支持受托管的语言(例如Visual Basic.NET和C#)对数据的访问,而创建ADO更多的是为了支持本地语言。ADO.NET的原理与ADO的原理有些不同:ADO.NET在“非连接”的模式下工作,在这种模式下无需保持与服务器的活跃连接就可以对数据进行访问和操作。当完成工作时,可以建立连接,然后将所有合适的更新传送给服务器(假设服务器支持这种行为)。
ADO.NET比ADO更模块化。ADO工作的方式比较单一,通过特殊代码与比其他提供程序更好的SQL Server提供程序进行交互。ADO.NET提供了使用任何OLE DB提供程序的通用对象,但是也支持提供程序创建它们自己的、用于数据交互的受托管的提供程序。例如,SQLADO.NET包含一些对象,这些对象对专门与SQL Server进行的交互进行了优化。此外,可以为任何数据源编写类似的受托管的提供程序。
与ADO相似,ADO.NET包含连接对象和命令对象。然而,ADO.NET引入了用于数据交互的数据集对象。数据集是包含在一组数据表中的服务器数据的缓存,这些数据表可以独立地进行更新或者归档为XML。数据集是使用数据适配器进行加载的——该适配器可以是ADO.NET提供的通用适配器,也可以是特定于提供程序的适配器,例如SQLDataAdapter。为了支持对数据的直接访问,ADO.NET使用从它的命令对象中返回的数据阅读器,该数据阅读器在概念上与ADO记录集类似。
14.1.3 ADOMD.NET
ADOMD.NET(多维的ADO.NET)是受托管的数据提供程序,它实现了ADO.NET专门用于Analysis Services的数据适配器和数据阅读器接口,从而使得它比通用的ADO.NET对象更快,而且在内存利用上更有效。除了标准的ADO.NET接口,ADOMD.NET还包含数据挖掘对象和特定于OLAP的对象,从而使得对数据挖掘客户端应用程序进行编程变得更容易。
MiningStructure、MiningModel和MiningColumn集合使得提取(用来描述服务器上的对象的)元数据变得容易。MiningContentNode对象支持通过编程来浏览挖掘模型,并且可以从内容层次的根上或者随机地从内容中的任何节点上访问该对象。
注意:
ADOMD.NET也存在本地版本,被恰当地命名为ADOMD。保留该接口主要是为了支持向后与SQL Server 2000兼容,该接口不包含用于数据挖掘编程的任何对象或者接口。
14.1.4 Server ADOMD
Server ADOMD是用于直接在服务器上访问Analysis Server对象的对象模型,这些对象可以是数据挖掘对象,也可以是OLAP对象。Server ADOMD在用户自定义的函数中使用,本章后面将进行论述。
14.1.5 AMO
AMO,即分析管理对象(Analysis Management Object),是Analysis Services的主要管理接口。它取代了SQL Server 2000的接口——决策支持对象(Decision Support Object,DSO),为了支持向后兼容,所以仍然保留了该接口,但是没有对它进行更新来使它利用SQL Server 2005的所有新特性。
像ADOMD.NET一样,AMO包含了MiningStructures、MiningModels和MiningColumns集合等等。然而,ADOMD.NET用于浏览和查询操作,而AMO用于创建和管理操作。在BI Workbench或者SQL Workbench的用户界面中执行的所有操作都可以通过使用AMO编程来执行;事实上,它们的用户界面的管理操作都是使用AMO来编写的。
提示:
当编写数据挖掘客户端应用程序时,应该使用ADOMD.NET(除非.NET不可用)。否则,对于Windows应用程序而言,应该使用ADO(或者OLE DB);对于瘦客户端应用程序而言,应该使用简单的XMLA。对于用来创建新的模型或者管理现有模型的应用程序而言,需要使用AMO。
注意:
要获得Analysis Services使用的所有API的样例及其完整文档,可以参考SQL Server联机丛书。
14.2 使用Analysis Services的API
在必须访问Analysis Services的任何API的时候,必须确保添加了适合于项目的引用。表14-2列出了许多API及其所需的引用。
表14-2 Analysis Services引用
|
API |
类 型 |
引 用 |
|
ADO |
本地 |
Microsoft ActiveX Data Objects |
|
ADOMD.NET |
托管 |
Microsoft.AnalysisServices.AdomdClient |
|
Server ADOMD |
托管 |
Microsoft.AnalysisServices.AdomdServer |
|
AMO |
托管 |
Microsoft.AnalysisServices Microsoft.DataWarehouse.Interfaces |
|
DSO |
本地 |
Microsoft Decision Support Objects 9 |
要使编码变得更容易,则可以将如下所示的代码加入到源文件的顶部,从而不需要为每个对象指定完全限定名。
VB.NET
Imports Microsoft.AnalysisServices
C#
Using Microsoft.AnalysisServices
14.3 使用Microsoft.AnalysisServices创建和管理挖掘模型
在本节中,将使用AMO来创建和管理模型,这些模型使用MovieClick数据库来分析不同代龄(generation)的客户对额外付费频道的使用情况。如果您的编程兴趣只集中于将数据挖掘嵌入到客户端应用程序中,则可以跳过本节。
创建挖掘模型最简单的方法是通过任何命令接口(例如ADO、ADO.NET或者ADOMD.NET)来使用DMX语句(例如CREATE MINING MODEL和INSERT INTO)。虽然这种方法具有简单性的优势,但是不能通过基于命令的API来访问某些特性,例如, 数据挖掘钻取和定制列绑定。因此,要确保应用程序可以利用SQL Server数据挖掘提供的所有特征,推荐使用AMO作为创建挖掘模型的API。事实上,在BI Development Studio和SQL Management Studio中包括的创建、编辑和管理工具都是通过AMO来编写的。第16章将描述适合于使用DMX来进行模型创建和处理的场景。
图14-1显示了用于数据挖掘编程的主要AMO对象。在本章有关AMO的内容中的样例代码会用到这些对象。
图14-1 部分的AMO对象层次
14.3.1 AMO的基本原理
AMO是非常简单的对象模型,该模型位于Analysis Services对象的XML表示的顶部。除了提供方便的API之外,AMO还提供了基本的验证机制和方法来更新、修改和监控服务器上的对象。
注意:
要将AMO代码加入到项目中,必须将引用加入到两个程序集中:Microsoft.AnalysisServices和Microsoft.DataWarehouse.Interfaces。要使编码变容易,可以将如下代码加入到源文件的顶部,从而不需要为每个对象指定完全限定名。
VB.NET
Imports Microsoft.AnalysisServices
C#
Using Microsoft.AnalysisServices
在AMO中的每个对象都实现了NamedComponent接口,该接口提供了Name属性、Id属性和Description属性以及Validate方法。对象的ID是一旦设置了就不可改变的标识符。例如,当使用固定对象来开发用户的应用程序时,这一点比较有用。用户可以根据自己的使用情况来任意地修改对象名,同时,在引用对象时为代码提供了一致的引用方式。
MajorObject是从NamedComponent继承过来的,并且增加了Update方法和Refresh方法,这些方法分别通过本地的修改来更新服务器以及通过服务器的内容来更新本地的模型。另外,MajorObject包含一些方法(这些方法用于访问引用的和依赖的对象)和Annotations集合(该集合用于任意的用户扩展)。Role对象是MajorObject的一个示例。
ProcessableMajorObject从MajorObject继承而来,并且增加了一些方法和属性来处理对象以及确定处理的状态和最后的处理时间。MiningStructure是ProcessableMajorObject的一个示例。
14.3.2 AMO应用程序和安全
因为AMO通常是托管的API,所以必须将特定的权限提供给用户,从而使用户可以使用任何基于AMO的应用程序。显然,拥有Administrator权限的任何用户都将可以访问任何AMO应用程序,但是,其权限有较多限制的用户在进行访问时也会受到限制。
表14-3描述了用户通过AMO来执行任何功能时所需要的权限。
注意:
使用AMO来执行某些操作(例如迭代对象)所需要的权限级别比使用命令API(例如ADOMD.NET)所需要的权限级别要高。这是因为ADOMD.NET和其他的API使用数据库模式而不是元数据的定义来访问对象。
表14-3 AMO的权限
|
目 的 |
需要的权限 |
|
迭代对象 |
Access和Read Definition |
|
查看对象的定义 |
Access和Read Definintion |
|
修改对象 |
Administrator |
|
处理对象 |
Access、Read Definition和Process |
|
增加或者删除对象 |
Administrator |
|
设置权限 |
Administrator |
|
接收跟踪信息 |
Administrator |
提示:
可以通过模仿一些角色或者特定的用户来测试应用程序中的安全性。在连接字符串中,将Effective Roles属性设置为所希望模仿的一组用逗号分开的角色,或者将Effective Username连接字符串属性设置为该用户的名字。需要指出的是,只有服务器的管理员才可以连接这些属性。
例如:
svr.Connect("location=localhost;" _ &
"Initial Catalog=MyDatabase;Effective Roles=LimitedAccessRole")
14.3.3 对象的创建
要通过使用AMO编程来创建挖掘模型,需要执行的所有步骤与在用户界面中创建和管理模型的步骤相同。也就是说,如第3章所描述的,创建数据库、数据源、数据源视图和挖掘结构,最后创建挖掘模型。
要在服务器上创建任何对象,通常执行以下步骤:
(1) 实例化对象。
(2) 设置对象的Name属性和ID属性。
(3) 设置特定于对象的属性。
(4) 将对象加入到它的父容器中。
(5) 调用对象(或它的父对象)的Update方法。
例如,代码清单14-1示范了如何连接到本地服务器,然后创建数据库。
代码清单14-1 数据库的创建
Sub CreateDatabase()
Dim svr As Server
Dim db As Database
\' Create server object and connect
svr = New Server()
svr.Connect("location=localhost")
\' Create database and set properties
db = New Database()
db.Name = "MovieClick"
db.ID = "MovieClick"
\' Add database and commit to server
svr.Databases.Add(db)
db.Update()
\' Disconnect from server
svr.Disconnect()
End Sub
1. 创建数据访问对象
在拥有数据库对象之后,下一步是创建数据源对象和数据源视图(DSV)对象。数据源对象相当简单,只由用来连接数据库的连接字符串组成,而DSV稍微复杂一些。DSV主要的元素是模式,模式是增加了自定义属性的标准Dataset对象。
为了将模式加载到DSV中,则要为希望加载的每个表创建数据适配器,然后将这些表的模式加入到某个数据集中。然后增加任何必需的关系,最后将数据集加入到DSV中(然后,DSV会被加入到AMO数据库中)。代码清单14-2通过创建用于MovieClick数据的数据源和DSV来示范该过程,该DSV可以用来创建包含嵌套表(需要用该嵌套表来对电影频道进行分析)的挖掘模型。
代码清单14-2 数据访问对象的创建
Sub CreateDataAccessObjects(ByVal db As Database)
\' Create relational datasource
Dim ds As New RelationalDataSource("MovieClick","MovieClick")
ds.ConnectionString = "Provider=SQLOLEDB;Data Source=localhost;" & _
"Initial Catalog=MovieClick;Integrated Security=True"
db.DataSources.Add(ds)
\' Create connection to datasource to extract schema to dataset
Dim dset As New DataSet()
Dim cn As New SqlConnection("Data Source=localhost;" & _
"Initial Catalog=MovieClick;Integrated Security=True")
\' Create data adapters from database tables and load schemas
Dim daCustomers As New SqlDataAdapter("Select * from Survey", cn)
daCustomers.FillSchema(dset, SchemaType.Mapped, "Customers")
Dim daChannels As New SqlDataAdapter("Select * from Channels", cn)
daChannels.FillSchema(dset, SchemaType.Mapped, "Channels")
\' Add relationship between Customers and Channels
Dim drCustomerChannels As New DataRelation("CustomerChannels", _
dset.Tables("Customers").Columns("SurveyTakenID"), _
dset.Tables("Channels").Columns("SurveyTakenID"))
dset.Relations.Add(drCustomerChannels)
\' Create the DSV, add the dataset, and add to the database
Dim dsv As New RelationalDataSourceView("MovieClick", "MovieClick")
dsv.DataSourceID = "MovieClick"
dsv.Schema = dset.Clone()
\' Update the database to create the objects on the server.
db.Update(UpdateOptions.ExpandFull)
End Sub
代码清单14-2的DSV包含了客户表和频道表,但是我们希望构建的模型需要的具体信息比原始数据中所给出的具体信息要多:客户的“代龄(generation)”和只包含他们观看的额外付费电影频道的清单。要完成该操作,则必须对代码进行修改:将计算列加入到Customers表中,然后通过命名查询来交换出Channels表(该查询只返回我们感兴趣的有限的一组频道)。
代码清单14-3包含了通过命名计算和命名查询来修改的CreateDataAccessObjects。
代码清单14-3 创建计算列和命名查询
Sub CreateDataAccessObjects(ByVal db As Database)
\' Create relational datasource
Dim ds As New RelationalDataSource("MovieClick","MovieClick")
ds.ConnectionString = "Provider=SQLOLEDB;Data Source=localhost;" & _
"Initial Catalog=MovieClick;Integrated Security=True"
db.DataSources.Add(ds)
\' Create connection to datasource to extract schema to dataset
Dim dset As New DataSet()
Dim cn As New SqlConnection("Data Source=localhost;" & _
"Initial Catalog=MovieClick;Integrated Security=True")
\' Create the customers data adapter with the
\' calculated column appended
Dim daCustomers As New SqlDataAdapter("SELECT *, " & _
"(CASE WHEN (Age < 30) THEN \'GenY\' " & _
" WHEN (Age >= 30 AND Age < 40) THEN \'GenX\' " & _
" ELSE \'Baby Boomer\' END) AS Generation" & _
"FROM Customers", cn)
daCustomers.FillSchema(dset, SchemaType.Mapped, "Customers")
\' Add extended properties to the Generation column
\' indicating to AnalysisServices that it is a
\' calculated column.
dset.Tables("Customers").Columns("Generation"). _
ExtendedProperties.Add("DbColumnName", "Generation")
dset.Tables("Customers").Columns("Generation"). _
ExtendedProperties.Add("Description", _
"Customer Generation")
dset.Tables("Customers").Columns("Generation"). _
ExtendedProperties.Add("IsLogical", "True")
dset.Tables("Customers").Columns("Generation"). _
ExtendedProperties.Add("ComputedColumnExpression", _
"CASE WHEN (Age < 30) THEN \'GenY\' " & _
" WHEN (Age >= 30 AND Age < 40) THEN \'GenX\' " & _
" ELSE \'Baby Boomer\' END")
\'Create a \'pay channels\' data adapter with a custom query
\' for our named query.
Dim daPayChannels As New SqlDataAdapter("SELECT * FROM Channels "& _
"WHERE Channel IN (\'Cinemax\', \'Encore\', \'HBO\', \'Showtime\', " & _
"\'STARZ!\', \'The Movie Channel\')", cn)
daPayChannels.FillSchema(dset, SchemaType.Mapped, "PayChannels")
\' Add extended properties to the PayChannels table
\' indicating to AnalysisServices that it is a
\' named query.
dset.Tables("PayChannels"). _
ExtendedProperties.Add("IsLogical", "True")
dset.Tables("PayChannels"). _
ExtendedProperties.Add("Description", _
"Channels requiring an additional fee")
dset.Tables("PayChannels"). _
ExtendedProperties.Add("QueryDefinition", _
"SELECT * FROM Channels WHERE Channel IN (\'Cinemax\', " & _
"\'Encore\', \'HBO\', \'Showtime\', \'STARZ!\', \'The Movie Channel\')")
dset.Tables("PayChannels"). _
ExtendedProperties.Add("TableType", "View")
\' Add relationship between Customers and PayChannels
Dim drCustomerPayChannels As New DataRelation("CustomerPayChannels",_
dset.Tables("Customers").Columns("SurveyTakenID"), _
dset.Tables("PayChannels").Columns("SurveyTakenID"))
dset.Relations.Add(drCustomerPayChannels)
\' Create the dsv, add the dataset, and add to the database
Dim dsv As New RelationalDataSourceView("MovieClick", "MovieClick")
dsv.DataSourceID = "MovieClick"
dsv.Schema = dset.Clone()
db.DataSourceViews.Add(dsv)
\' Update the database to create the objects on the server.
db.Update(UpdateOptions.ExpandFull)
End Sub
2. 创建挖掘结构
在数据挖掘编程中的下一步是创建挖掘结构,该挖掘结构描述了数据挖掘引擎可以理解的问题领域。必须创建MiningStructureColumns,然后指定它们的数据类型、内容类型以及与DSV中的源列的数据绑定。代码清单14-4包含用于创建挖掘结构的代码,通过该挖掘结构可以分析代龄和额外付费频道之间的关系。
代码清单14-4 创建挖掘结构
Sub CreateMiningStructure(ByVal db As Database)
\' Initialize our Mining Structure
Dim ms As New MiningStructure("PayChannelAnalysis", _
"PayChannelAnalysis")
ms.Source = New DataSourceViewBinding("MovieClick")
\' Create the columns of the MiningStructure,
\' setting the type, content, and data binding.
\' UserID column
Dim UserID As New ScalarMiningStructureColumn("UserId", "UserId")
UserID.Type = MiningStructureColumnTypes.Long
UserID.Content = MiningStructureColumnContents.Key
UserID.IsKey = True
\' Add data binding to the column.
UserID.KeyColumns.Add("Customers", "UserId", OLE DBType.Integer)
\' Add the column to the MiningStructure
ms.Columns.Add(UserID)
\' Generation column
Dim Generation As New ScalarMiningStructureColumn _
("Generation", "Generation")
Generation.Type = MiningStructureColumnTypes.Text
Generation.Content = MiningStructureColumnContents.Discrete
Generation.KeyColumns.Add("Customers", "Generation", _
OleDbType.VarChar)
\' Add the column to the MiningStructure.
ms.Columns.Add(Generation)
\' Add Nested Table by creating a table column
\' and adding a key column to the nested table.
Dim PayChannels As New TableMiningStructureColumn _
("PayChannels", "PayChannels")
Dim Channel As New ScalarMiningStructureColumn _
("Channel", "Channel")
Channel.Type = MiningStructureColumnTypes.Text
Channel.Content = MiningStructureColumnContents.Key
Channel.IsKey = True
Channel.KeyColumns.Add("PayChannels", "Channel", OleDbType.VarChar)
PayChannels.Columns.Add(Channel)
ms.Columns.Add(PayChannels)
\' Add the MiningStructure to the database.
db.MiningStructures.Add(ms)
ms.Update()
End Sub
注意:
您可能想知道为什么指明列的内容是Key,却还必须将IsKey属性设置为True。这归因于在OLE DB for Data Mining规范中所定义的内容类型的可扩展性。当前Analysis Services支持三种类型的键:Key、Key Time和Key Sequence。通过使用一个单独的IsKey属性,可以在将来利用这一可扩展性。
3. 创建挖掘模型
最后,应该可以创建模型来分析客户。除了列的集合,结构还包含了模型集合。对于每个模型,根据需要从结构中增加列,然后将它们的用法设置为Key、Predict或者PredictOnly。对于没有指定用法的列,假定其用法为Input,所以不需要显式地设置它们。对于希望算法忽略的列,不将其加入模型即可。
代码清单14-5示范了如何在前面创建的结构中创建两个模型。该示例创建参数化的聚类模型,然后从该模型的副本中构建一个树模型。
代码清单14-5 向结构中添加挖掘模型
Sub CreateModels(ByVal ms As MiningStructure)
Dim ClusterModel As MiningModel
Dim TreeModel As MiningModel
Dim mmc As MiningModelColumn
\' Create the Cluster model and set the
\' algorithm and parameters.
ClusterModel = ms.CreateMiningModel(True, _
"Premium Generation Clusters")
ClusterModel.Algorithm = "Microsoft_Clusters"
ClusterModel.AlgorithmParameters.Add("CLUSTER_COUNT", 0)
\' Add the case key - every model must contain the case key.
mmc = ClusterModel.Columns.Add("UserID")
mmc.SourceColumnID = "UserID"
mmc.Usage = "Key"
\' Add the Generation column.
mmc = ClusterModel.Columns.Add("Generation")
mmc.SourceColumnID = "Generation"
\' Add the nested table.
mmc = ClusterModel.Columns.Add("PayChannels")
mmc.SourceColumnID = "PayChannels"
\' Add the nested key – required for nested tables
mmc = mmc.Columns.Add("Channel")
mmc.SourceColumnID = "Channel"
mmc.Usage = "Key"
\' Copy the cluster model and change the necessary properties
\' to make it a tree model to predict Generation.
TreeModel = ClusterModel.Clone()
TreeModel.Name = "Generation Trees"
TreeModel.ID = "Generation Trees"
TreeModel.Algorithm = "Microsoft_Decision_Trees"
TreeModel.AlgorithmParameters.Clear()
TreeModel.Columns("Generation").Usage = "Predict"
TreeModel.Columns("PayChannels").Usage = "Predict"
ms.MiningModels.Add(TreeModel)
\' Submit the models to the server.
ClusterModel.Update()
TreeModel.Update()
End Sub
4. 处理挖掘模型
用来处理对象的代码比较简单,代码中只包含Process方法,调用该方法时需要合适的选项。在示例程序中,可以根据选择来处理单个模型、挖掘结构或者整个数据库。然而,因为处理是相当耗时的任务,所以最好在这段时间内从服务器上接收进度消息。幸运的是,AMO包含了Trace对象来处理这种类型的服务器交互。代码清单14-6示范了为处理操作设立进度跟踪。
代码清单14-6 在处理数据库的同时提供进度报告信息
Sub ProcessDatabase(ByVal svr As Server, ByVal db As Database)
Dim t As Trace
Dim e As TraceEvent
\' Create the trace object to trace progress reports
\' and add the column containing the progress description.
t = svr.Traces.Add()
e = t.Events.Add(TraceEventClass.ProgressReportCurrent)
e.Columns.Add(TraceColumn.TextData)
t.Update()
\' Add the handler for the trace event.
AddHandler t.OnEvent, AddressOf ProgressReportHandler
Try
\' Start the trace process of the database, then stop it.
t.Start()
db.Process(ProcessType.ProcessFull)
t.Stop()
Catch ex As Exception
End Try
\' Remove the trace from the server.
t.Drop()
End Sub
Sub ProgressReportHandler(ByVal sender As Object, _
ByVal e As TraceEventArgs)
lblProgress.Text = e(TraceColumn.TextData)
End Sub
5. 部署挖掘模型
在创建模型之后,需要将这些模型移到不同的服务器;例如,您可能需要将它们从分析服务器移到产品服务器,从而将它们嵌入到各个行业的应用程序中,或者可能与同事共享模型,该同事不能够在物理上访问您的服务器。
确定服务器的能力
当在服务器上创建模型时,准确地理解可以创建什么类型的模型非常有用。在SQL Server的标准版和企业版中,可供选择的算法不同,而且可能插入了不同的算法。另外,每个算法支持不同的参数,这些参数的默认值可能因为服务器的配置而有所不同。
在第2章中描述的MINING_SERVICES和MINING_PARAMETERS模式行集包含了可用算法及其功能的描述信息。可以使用任何客户端命令API来访问这些模式,或者更好的是,可以通过使用ADOMD.NET中提供的对象模型来对服务器的数据挖掘功能进行快速迭代。以下代码示范了如何对挖掘服务及其各自的参数进行迭代。
Sub DiscoverServices()
Dim cn As New AdomdConnection("location=localhost")
Dim ms As MiningService
Dim mp As MiningServiceParameter
cn.Open()
For Each ms In cn.MiningServices
Console.WriteLine("Service: " & ms.Name)
For Each mp In ms.AvailableParameters
Console.WriteLine(" Parameter: " & mp.Name & _
" Default: " & mp.DefaultValue)
Next
Next
cn.Close()
End Sub
Analysis Services在AMO中提供了健壮的备份和恢复API。然而,这些API更多的是面向OLAP对象的,而不是面向数据挖掘对象的。API包含许多选项,这些选项不是数据挖掘所必需的,同时只单独在数据库级别上进行操作,而这通常对于大多数数据挖掘操作来说级别都太低。
由于在AMO中提供的功能和需要的功能之间存在不匹配的情况,所以数据挖掘对象的部署是通过使用命令API的DMX来处理的。使用DMX EXPORT命令和IMPORT命令,可以选择单个模型(该模型在创建的大量候选模型中性能最好),然后单独对它进行部署,而不是部署整个数据库。代码清单14-7示范了使用ADOMD来将个别的模型从当前的服务器传送到产品服务器上。
代码清单14-7 挖掘模型的导出和导入
Sub TransferModel()
\' Create connections to the source and destination server.
Dim cnSource As New AdomdConnection("location=localhost;" & _
"Initial Catalog=MovieClick")
Dim cnDest As New AdomdConnection("location=ProductionServer;" & _
"Initial Catalog=MovieClick")
Try
\' Export the model to a share on the destination server.
Dim cmdExport As New AdomdCommand
cmdExport.Connection = cnSource
cmdExport.CommandText = "EXPORT MINING MODEL GenerationTree " & _
"TO \'""ProductionServer"Transfer"GenerationTree.abk\' " & _
"WITH PASSWORD= \'MyPassword\'"
cnSource.Open()
cmdExport.ExecuteNonQuery()
\' Import the model into the current database on the
\' destination server.
Dim cmdImport As New AdomdCommand
cmdImport.Connection = cnDest
cmdImport.CommandText = "IMPORT FROM " & _
" c:"Transfer"GenerationTree.abk " & _
" WITH PASSWORD= \'MyPassword\' "
cnDest.Open()
cnDest.ExecuteNonQuery()
Catch ex As Exception
End Try
cnSource.Close()
cnDest.Close()
End Sub
在这个示例中,只需要在服务器之间移动一个模型。EXPORT命令足够灵活,从而可以导出多个模型,也可以导出整个挖掘结构。如果必须在目标服务器上重新处理模型,则可以将INCLUDE DEPENDENCIES追加到EXPORT命令中,从而在导出包中包括必需的数据源对象和DSV对象。
注意:
由于OLAP对象不支持对象级别的导入和导出,所以不可以使用EXPORT命令来导出OLAP挖掘模型。
6. 设置挖掘权限
在构建、处理和部署了模型之后,必须指派权限,从而可以通过客户端应用程序来访问这些模型。在Analysis Services中的权限是通过两个对象的协调来管理的:Role对象(该对象属于数据库并且包含一系列成员)和Permission对象(该对象属于受保护的对象,并且引用角色和指定该角色的访问权限)。代码清单14-8示范了创建一个角色以及指派一些权限。
代码清单14-8 指派挖掘模型的权限
Sub SetModelPermissions(ByVal db As Database, ByVal mm As MiningModel)
\' Create a new role and add members.
Dim r As New Role("ModelReader", "ModelReader")
r.Members.Add("MOVIECLICK"Jamiemac")
r.Members.Add("MOVIECLICK"Zhaotang")
\' Add the role to the database and update
db.Roles.Add(r)
r.Update()
\' Create a permission object referring to the role.
Dim mmp As New MiningModelPermission()
mmp.Name = "ModelReader"
mmp.ID = "ModelReader"
mmp.RoleID = "ModelReader"
\' Assign access rights to the permission.
mmp.Access = Access.Read
mmp.AllowBrowsing = True
mmp.AllowDrillThrough = True
mmp.AllowPredict = True
\' Add permissions to the model and update
mm.MiningModelPermissions.Add(mmp)
mm.Update()
End Sub
14.4 浏览和查询挖掘模型
创建和部署模型只是开始。当使用模型学习到的知识并且直接将这些知识嵌入到应用程序中时,真正的工作才开始。您可以推荐产品、管理存货、预测税收、验证数据以及执行无数的其他任务,这些任务只受到您的数据和想象力的限制。
14.4.1 使用ADOMD.NET来预测
让我们从使用ADOMD.NET的基本预测查询的示例开始。代码清单14-9示范了执行查询的典型示例。熟悉ADO.NET的读者将会注意到,迄今为止,API之间的唯一区别在于数据访问类的名称。事实上,使用ADO.NET类来针对Analysis Services执行简单的查询也同样是可能的,然而,ADOMD.NET得到了优化,以便使用Analysis Services服务器,从而可以利用额外的Analysis Services特性。
代码清单14-9 执行简单的单例预测查询
Private Sub SingleResultQuery()
\' Create connection and command objects.
Dim cn As New AdomdConnection("location=localhost; " & _
"Initial Catalog=MovieClick")
Dim cmd As New AdomdCommand()
\' Initialize command with query
cmd.Connection = cn
cmd.CommandText = "SELECT Predict(Generation) " & _
"FROM [Generation Trees] NATURAL PREDICTION JOIN " & _
"SELECT (SELECT \'HBO\' AS Channel UNION " & _
"SELECT \'Showtime\' AS Channel) as PayChannels as t"
\' Open connection and write result to debug window
cn.Open()
Debug.WriteLine(cmd.ExecuteScalar().ToString())
\' Close connection
cn.Close()
End Sub
提示:
当执行单例查询(该查询返回单行中的单列)时使用ExecuteScalar方法。
当执行返回多个列或者行的查询(如代码清单14-10所示)时使用ExecuteReader,该查询执行与代码清单14-9相同的预测,但是返回PredictHistogram的平坦化结果,所以我们可以看到所有可能的预测结果的似然估计。
代码清单14-10 对包含多行的结果进行迭代
Private Sub MultipleRowQuery()
\' Create connection and command objects.
Dim cn As New AdomdConnection("location=localhost;" & _
"Initial Catalog=MovieClick")
Dim cmd As New AdomdCommand()
\' Initialize command with query
cmd.Connection = cn
cmd.CommandText="SELECT FLATTENED PredictHistogram(Generation) " & _
"FROM [Generation Trees] NATURAL PREDICTION JOIN " & _
"SELECT (SELECT \'HBO\' AS Channel UNION " & _
"SELECT \'Showtime\' AS Channel) as PayChannels as t"
\' Open connection and execute query
Dim reader As AdomdDataReader
cn.Open()
reader = cmd.ExecuteReader()
\' Write field names to debug window
Dim i As Integer
For i = 0 To reader.FieldCount - 1
Debug.Write(reader.GetName(i) & ""t")
Next
Debug.WriteLine("")
\' Iterate results to debug window
While reader.Read
For i = 0 To reader.FieldCount - 1
Debug.Write(reader.GetValue(i).ToString())
Next
Debug.WriteLine("")
End While
\' Close reader and connection
reader.Close()
cn.Close()
End Sub
在最后一个示例中,为了方便迭代而将嵌套表查询的结果平坦化。然而,在一些情况下,对结果进行平坦化是不切实际的,例如,当查询返回多个嵌套表乃至嵌套表内的嵌套表时。代码清单14-11示范了如何在不含FLATTENED关键字的情况下对前面示例的结果进行迭代。
代码清单14-11 对嵌套的PredictHistogram结果的Attribute列进行迭代
Dim nestedreader As AdomdDataReader
While reader.Read()
nestedreader = reader.GetReader(0)
While nestedreader.Read()
Debug.WriteLine(nestedreader.GetValue(0).ToString())
End While
nestedreader.Close()\' Be sure to close the nested readers!
End While
到目前为止,所有通过ADO.NET来执行的操作都执行完了,但还不够有效。下一步,学习通过以下方式来扩展应用程序的功能:使用参数化的查询来修改预测的输入。ADO.NET不支持命名参数,这些参数用于提供程序而不是SQL Server关系引擎。要在查询中使用命名参数,则必须使用ADOMD.NET。代码清单14-12示例了使用命名参数的数据挖掘查询。
代码清单14-12 使用命名参数的数据挖掘查询
\' Initialize command with parameterized query
cmd.CommandText = "SELECT PredictHistogram(Generation) " & _
"FROM [Generation Trees] NATURAL PREDICTION JOIN " & _
"SELECT (SELECT @Channel1 AS Channel UNION " & _
"SELECT @Channel2 AS Channel) as PayChannels as t"
\' Initialize parameters and add to command
Dim Channel1 As New AdomdParameter()
Dim Channel2 As New AdomdParameter()
Channel1.ParameterName = "@Channel1"
Channel2.ParameterName = "@Channel2"
cmd.Parameters.Add(Channel1)
cmd.Parameters.Add(Channel2)
\' Set parameter values
cmd.Parameters("@Channel1").Value = "HBO"
cmd.Parameters("@Channel2").Value = "Showtime"
代码清单14-12假定我们知道只允许和需要两个频道来执行该预测。显然,情况并不总是这样。ADOMD.NET允许使用参数来将整个表作为输入数据源传入。通过这一点,我们可以比较容易地使用数据来执行预测,这些数据在客户端上或者对服务器不可用。代码清单14-13示范了使用整形(shaped)后的表参数来作为预测的输入。
代码清单14-13 使用表参数的数据挖掘查询
\' Create table for case
Dim caseTable as new DataTable
caseTable.Columns.Add("CustID", System.Type.GetType("System.Int32"))
caseTable.Rows.Add(1)
\' Create nested table
Dim nestedTable as new DataTable
nestedTable.Columns.Add("CustID", _
System.Type.GetType("System.Int32"))
nestedTable.Columns.Add("Channel", _
System.Type.GetType("System.String"))
nestedTable.Rows.Add(1,"HBO")
nestedTable.Rows.Add(1,"Showtime")
\' Initialize command with parameterized query
cmd.CommandText = "SELECT PredictHistogram(Generation) " & _
"FROM [Generation Trees] NATURAL PREDICTION JOIN " & _
"SHAPE { @CaseTable } " & _
"APPEND ({ @NestedTable } " & _
"RELATE CustID to CustID) AS Channels " & _
"as t"
\' Initialize parameters and add to command
Dim caseParam As New AdomdParameter()
Dim nestedParam As New AdomdParameter()
caseParam.ParameterName = "@CaseTable"
nestedParam.ParameterName = "@NestedTable"
cmd.Parameters.Add(caseParam)
cmd.Parameters.Add(NestedParam)
\' Set parameter values
cmd.Parameters("@CaseTable").Value = caseTable
cmd.Parameters("@NestedTable").Value = nestedTable
14.4.2 浏览模型
正如在第2章中所描述的,所有的模型元数据和内容都可以通过模式行集来访问。然而,使用ADOMD.NET,可以改为使用丰富的对象模型来浏览服务器和模型。图14-2显示了ADOMD.NET的主要的数据挖掘对象。
图14-2 在ADOMD.NET中的数据挖掘对象层次
正如从对象模型中可以看到的,不需要求助于模式查询就可以连接到服务器,然后在数据挖掘对象上进行迭代。这为应用程序开发人员带来的好处是,如果已连接的用户没有访问特定对象的权限,则该对象将不会出现在它的集合中,正如它不存在一样。
通过使用ADOMD.NET对象模型所获得的最重要的能力是以自然的、层次化的方式来使用对象迭代模型内容的能力,而不是试图拆开平坦的模式行集表。使用该对象模型,使得编写复杂的程序来浏览内容或者将内容显示给用户变得容易。例如,Microsoft决策树算法重要的问题是:给定一个属性,查找在该属性上包含一个拆分的所有树。
代码清单14-14示范了使用内容对象模型来浏览树,以查找给定属性上的拆分。首先,对代表树的根的所有子节点进行识别,然后递归地检查树的子节点,以了解它们的边缘规则是否包含所请求的属性。通过查看节点类型,而不是使用的算法,该函数将针对包含树的任何模型进行处理,而不管它是使用Microsoft决策树算法、Microsoft时序算法,还是任何的第三方基于树的算法。
代码清单14-14 使用ADOMD.NET来浏览模型内容
\' Identify all the attributes that split
\' on a specified attribute.
Sub FindSplits(ByVal cn As AdomdConnection, _
ByVal ModelName As String, ByVal AttributeName As String)
\' Find the specified model.
Dim model As MiningModel
model = cn.MiningModels(ModelName)
If IsDBNull(model) Then Return
\' Look for the attribute in all model trees.
Dim node As MiningContentNode
For Each node In model.Content
If node.Type = MiningNodeType.Tree Then
FindSplits(node, AttributeName)
End If
Next
End Sub
\' Recursively search for the attribute among content nodes
\' Return when children exhausted or attribute is found
Sub FindSplits(ByVal node As MiningContentNode, _
ByVal AttributeName As String)
\' Check for the attribute in the MarginalRule.
If node.MarginalRule.Contains(AttributeName) Then
\' The attribute column contains the
\' name of the tree.
Debug.WriteLine(node.Attribute)
Return
End If
\' Recurse over child nodes
Dim childNode As MiningContentNode
For Each childNode In node.Children
FindSplits(childNode, AttributeName)
Next
End Sub
通过使用PredictNodeId函数,也可以使用模型内容来找出预测的理由。例如,可以使用以下查询:
SELECT Predict(Generation), PredictNodeId(Generation) ...
来检索用于生成预测结果的节点ID,然后将预测结果输入到类似如下的函数中:
Function GetPredictionReason(ByVal model As MiningModel, _
ByVal NodeID As String) As String
Dim node As MiningContentNode
node = model.GetNodeFromUniqueName(NodeID)
If IsDBNull(node) Then Throw New System.Exception("Node not found")
return node.Description;
End Function
14.4.3 存储过程
ADOMD.NET提供了一个优秀的对象模型,用于访问服务器对象和浏览模型内容。然而,该对象模型存在一些主要的缺点。对于在代码清单14-14中的FindSplits方法,必须将整个模型内容从服务器传送到客户端来确定拆分列表。包含1000棵树并且每棵树都包含1000个节点的模型需要的行数超过1 000 000,即使只有少数的树引用了所需要的属性。同样,在GetPredictionReason函数中,即使可以使用GetNodeFromUniqueName直接访问需要的节点,也会导致服务器在每次调用时发生往返操作;不推荐以批处理的方式来执行该操作。
对于这些问题存在解决方案。在SQL Server 2005中的Analysis Services支持存储过程,可以通过任何托管的语言(例如C#、VB.NET或者托管的C++)来编写存储过程。对象模型ADOMD+与ADOMD.NET几乎一样,它使得两个模型之间的转换变得容易。ADOMD+比较明显的优点是所有的内容都可以在服务器上获得,并且可以只将需要的信息返回给服务器。可以使用CALL语法或者将UDF作为DMX查询的一部分来单独调用UDF。例如,以下查询:
CALL MySprocs.TreeHelpers.FindSplits(\'Generation Trees\',\'HBO\')
直接调用存储过程,而且只是返回结果,然而下面这个查询:
SELECT Predict(Generation),
MySprocs.TreeHelpers.GetPredictionReason(PredictNodeId(Generation))
...
对于从预测查询中返回的每一行都调用存储过程。在这种情况下,查询将返回预测的结果以及对每一行结果的解释。
将VBA和Excel的函数作为存储过程来调用
如果在安装Analysis Services服务器的相同机器上安装了Microsoft Office,则可以将Visual Basic for Applications(VBA)和Excel的函数作为DMX查询内部的存储过程来利用。
例如,可以将预测的输出转换为小写形式,如下所示:
SELECT LCase(Predict(MyModel.[Home Ownership]) FROM MyModel
PREDICTION JOIN ....
如果函数同时存在于Excel和VBA中,则必须相应地使用Excel!和VBA!作为函数名的前缀。例如,要获得来自Excel的预测结果的以10为底的对数,以及来自VBA的预测结果的自然对数,则使用如下所示的查询:
SELECT Excel!Log(Predict(Sales)), VBA!Log(Predict(Sales))
From MyModel ....
如果Excel或者VBA函数也存在于MDX或者DMX中,或者该函数包含$字符,则必须用方括号([])转义函数名。例如,要将预测的格式设置为货币格式,例如$20.56,则使用如下所示的查询:
SELECT [Format](Predict(Sales), \'$d.dd\') FROM MyModel ....
在附录B中列出了所支持的VBA函数和Excel函数。
14.4.4 编写存储过程
在引用了需要的程序集——Microsoft.AnalysisServices.AdomdServer之后,可以访问称为Context的全局对象。该对象与ADOMD.NET连接对象相似,包含主要对象(例如MiningModels)的所有集合,在存储过程中可以访问这些对象。存储过程也可以利用Context对象的CurrentMiningModel属性来引用作为查询主体的模型。
存储过程可以采用任何简单的类型作为参数,并且可以在响应中返回简单的类型,甚至可以返回DataTable或者DataSet。如果客户端使用CALL来调用返回简单类型的存储过程,则尽管存储过程将会执行,也不会获得值。如果客户端调用预测查询内部的存储过程,并且该过程返回DataTable或者DataSet,则客户端将会获得包含返回行的嵌套表。
1. 存储过程与调用准备
当编写在服务器上执行的存储过程时,在准备调用期间,必须知道什么时候会调用存储过程来返回结果,以及什么时候调用存储过程只是为了收集模式信息。另外,在准备操作期间,必须指出,调用您的存储过程是安全的,并且调用它不会带来任何不需要的副作用,例如,您不希望两次都创建相同的对象。
将复杂的类型发送给存储过程
如果必须将复杂的类型(例如结构或者数组)发送给存储过程,则可以使用在客户端上的XMLSerializer来对它们进行串行化,然后将它们作为XML字符串发送出去。在服务器一方,反串行化结构或者数组,然后使用感兴趣的复杂类型来调用重载函数。例如,可能会有需要使用下列类型的数组作为参数的函数:
Public Structure MyType
Public a As Integer
Public b As String
End Structure
可以编写以下函数来将数组串行化为XML字符串,然后将该字符串作为参数发送给存储过程:
Function SerializeMyType(ByVal MyArray As MyType()) As String
Dim s As New
System.Xml.Serialization.XmlSerializer(MyArray.GetType())
Dim sw = New System.IO.StringWriter()
Dim str As String
s.Serialize(sw, MyArray)
Return sw.ToString()
End Function
在服务器一方,要复制类型的定义,然后编写一个存根函数来对数组进行反串行化,最后再调用实际的函数。
Public Function MySproc(ByVal xmlString As String) As DataTable
Dim MyArray() As MyType
Dim s As New
System.Xml.Serialization.XmlSerializer(MyArray.GetType())
Dim sr = New System.IO.StringReader(xmlString)
MyArray = s.Deserialize(sr)
Return MySproc(MyArray)
End Function
Function MySproc(ByVal MyArray As MyType()) As DataTable
... \' Function body
End Function
该策略将支持传送复杂的类型,而且为未来的版本(该版本可能支持自然地传送复杂的类型)做好了准备。
Context对象包含ExecuteForPrepare属性,可以在执行存储过程中的任何耗时操作之前对该属性进行检查。如果返回的是DataTable或者DataSet,则应该对这些对象进行定义,然后将空数据返回给它们,从而客户端将会知道模式。一般来说,在准备期间不应该发生错误,特别是缺少对象这类错误,因为准备调用可以在批处理查询期间调用,这些对象可能在调用该存储过程来返回结果之前都一直存在。
为了指出过程没有任何不期望的副作用,必须增加自定义的属性SafeToPrepare。
2. 存储过程示例
代码清单14-15示范了用VB.NET来编写的存储过程。其中的方法与代码清单14-13和代码清单14-14中的方法是相同的,但是对它进行了修改,用以在服务器上执行。此外,代码清单14-15考虑了Context对象的存在,同时考虑了怎样恰当处理在准备操作期间调用存储过程的场合。
代码清单14-15 数据挖掘存储过程
Imports Microsoft.AnalysisServices.AdomdServer
Imports System.Data
Public Class TreeHelper
<SafeToPrepare(True)> _
Public Function FindSplits(ByVal ModelID As String, _
ByVal AttributeName As String) As DataTable
\' Create the result table and add a column.
\' for the attribute
Dim tblResult As New DataTable()
tblResult.Columns.Add("Attribute", _
System.Type.GetType("System.String"))
\' If this is a prepare statement, return the empty
\' table for schema information.
If Context.ExecuteForPrepare Then Return tblResult
\' Access the model and throw an exception if not found.
\' Error text will be propagated to the client.
Dim model As MiningModel
model = Context.MiningModels(ModelID)
If IsDBNull(model) Then Throw _
New System.Exception("Model not found")
\' Look for the attribute in all model trees.
If model.Content.Count > 0 Then
Dim node As MiningContentNode
For Each node In model.Content(0).Children
If node.Type = MiningNodeType.Tree Then
FindSplits(node, AttributeName, tblResult)
End If
Next
End If
\' Return the table containing the result.
Return tblResult
End Function
Private Function FindSplits(ByVal node As MiningContentNode, _
ByVal AttributeName As String, _
ByRef tblResult As DataTable) As Boolean
\' Check for the attribute in the MarginalRule
\' and add row to the table if found
If node.MarginalRule.Contains(AttributeName) Then
Dim row() As String = {node.Attribute.Name}
tblResult.Rows.Add(row)
Return True
End If
\' Recurse over child nodes
Dim childNode As MiningContentNode
For Each childNode In node.Children
If (FindSplits(childNode, AttributeName, tblResult)) Then
Return True
End If
Next
Return False
End Function
<SafeToPrepare(True)> _
Public Function GetPredictionReason( _
ByVal NodeID As String) As String
\' Return immediately if executing for prepare
If Context.ExecuteForPrepare Then Return ""
\' Return the node description.
Return Context.CurrentMiningModel. _
GetNodeFromUniqueName(NodeID).Description
End Function
End Class
3. 在存储过程内部执行查询
存储过程一个常见的用途是封装查询,从而可以容易地重用该查询。例如,如果应用程序必须预测Generation,但是我们需要具有这样的灵活性:修改当前使用的模型或者增加其他业务逻辑,则可以通过编写存储过程来实现,该存储过程不需要修改应用层就可以根据需要来执行查询和重新部署过程。
Server ADOMD允许通过使用ADOMD.NET中使用的相同对象来执行DMX查询,唯一的区别在于不需要指定连接,因为已经连接上了。可以将来自查询的结果复制到DataTable,也可以只返回由ExecuteReader返回的DataReader。代码清单14-16示范了来自代码清单14-9的查询(该查询是作为UDF来实现的)。
代码清单14-16 执行存储过程内部的DMX查询
Imports Microsoft.AnalysisServices.AdomdServer
Imports System.Data
Public Class MyClass
<SafeToPrepare(True)> _
Public Function PredictGeneration() as AdomdDataReader
Dim cmd As New AdomdCommand()
\' Initialize command with query
cmd.CommandText = "SELECT Predict(Generation) " & _
"FROM [Generation Trees] NATURAL PREDICTION JOIN " & _
"SELECT (SELECT \'HBO\' AS Channel UNION " & _
"SELECT \'Showtime\' AS Channel) as PayChannels as t"
\' Return result to client
Return cmd.ExecuteReader()
End Sub
在这个示例中,如果希望修改正在执行预测的模型,则要修改在存储过程内部的查询,而不需要修改嵌入在应用程序内部的查询。当然,也可以参数化查询,如代码清单14-12所示。
注意:
存储过程不可以用来实现Analysis Serivices中的安全性。当前用户的安全上下文用来确定对Analysis Services服务器内部对象的访问。也就是说,任何调用过程来查询挖掘模型的用户如果在该模型上没有读的权限,则该用户将接收到权限错误消息。类似地,如果调用代码清单14-15中的GetPredictionReason UDF的用户在该模型上没有浏览权限,则也将接收到权限错误消息。
4. 部署和调试存储过程程序集
在编译和构建完存储过程之后,必须将存储过程部署到Analysis Server,从而可以在DMX中调用该存储过程。要将.NET程序集加入到Analysis Services项目中,则需要在解决方案资源管理器中右击“程序集”文件夹,然后选择“新建程序集”。
当部署项目时,会对程序集进行编码,然后将它发送给分析服务器,从而在项目数据库中可以使用该程序集。当必须更新程序集时,只需重新部署它。如果正在使用活跃的项目,则程序集会立刻部署在服务器上。如要在活跃的项目中更新程序集,则删除程序集,然后再将它添加回项目中。
如果您有通用的程序集,您希望在服务器上的所有数据库中都可以访问该程序集,则可以在服务器级别使用SQL Management Studio来部署该程序集。在对象资源管理器中,右击服务器的“程序集”集合。在该集合上,可以选择“新建程序集”,然后选择要增加的程序集。
最好是在同一台机器上运行服务器和客户端时对程序集进行调试;为了达到该目的,可以使用SQL Server的开发许可证。要在Visual Studio中调试程序集,则从“调试”菜单中选择“附加到进程”。从列表中选择可执行的msmdsrv.exe,然后确保对话框显示了与“附加到”相邻的Common Language Runtime。一旦遵循了这些步骤,就可以在存储过程中设置断点。