生活就好像一盒巧克力

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  26 随笔 :: 71 文章 :: 28 评论 :: 6 引用

公告

2007年1月25日 #

第一章 数据挖掘简介

    数据挖掘现在得到了越来越多商业机构的关注。现在人们经常说:“我们需要采用数据挖掘工具队我们的客户进行分类”,“数据挖掘能有效的增加客户的满意度”,甚至在说:“我们的竞争对手正在使用数据挖掘技术扩大市场份额——我们应当尽快的赶上去。”

    所以什么是数据挖掘?数据挖掘会给我们带来什么价值?该如何引入商务智能技术解决我们商务活动中的问题?数据挖掘究竟采用了哪些技术?数据挖掘项目的典型生命周期事怎样的?在本章中我们将回答上述的所有问题,并对数据挖掘世界进行较深入的介绍。在本章中我们将会学到如下内容:

  • 数据挖掘的定义
  • 明确哪些商务问题能用数据挖掘技术解决
  • 数据挖掘的任务
  • 采用不同的数据挖掘技术
  • 数据挖掘流程
  • 数据挖掘项目的生命周期
  • 当前的数据挖掘标准
  • 一些数据挖掘的发展方向

什么是数据挖掘

    数据挖掘是商务智能应用的关键组成部分。商务智能技术还包括:联机数据分析系统(OLAP)、企业报表和ETL(数据提取、转换和载入)系统。

    数据挖掘是指采用自动化或半自动化的技术手段,对数据进行分析,并发现数据中隐藏的潜在模式的过程。在过去的十几年中,大量的数据已经被采集并存储在数据库中。这些数据主要来源于商务管理软件,包括:财务系统、ERP系统、客户关系管理系统以及博客系统等。对这些数据的拥有,使企业变得富有数据而知识贫乏。当前,企业的数据量已经变的非常庞大,并且增长迅速,对这些数据的利用也变得越来越不现实。数据挖掘的主要目的是从已有的数据中提取模式,将数据变为知识,以增加数据的内在价值。你可能会问,用SQL从数据中提取知识可能吗?换句话说,你会想知道数据挖掘系统和关系型数据库系统的主要差别是什么呢?让我们看看下面的例子.

    图1.1显示了一个存有中学毕业生的关系型数据表。在数据表中存有性别、智商(IQ)、父母对孩子的鼓励程度、父母的收入、学生对读大学的意向。如果有人问你:是什么因素驱使一个中学毕业生选择读大学的呢?

    你可能会写一个查询,得出有多少男生毕业后上了大学,以及有多少女生毕业后上了大学。你也可能会写一个查询,得出父母的鼓励对孩子是否上大学的影响。那么父母的鼓励对男生的影响呢?父母的鼓励对女生的影响呢?你需要些成百上千的查询来覆盖这些可能的查询组合。对于采用数值形式的数据项[1](比如父母的收入、智商等)将会使分析变的更加困难。这时的查询可能会涉及到该数据项的任意取值区间。试想如果在你的数据表中有成百的数据列呢?你很快就会发现,你将面对一个无法处理的规模庞大的查询组合,来满足可能的查询需求。

    相反采用数据挖掘的方法将使这些问题变得非常的简单。你所需要做的仅仅是选择合适的数据挖掘算法,设置各数据列的使用方式(指:将数据列作为输入项还是预测项)。决策树模型是挖掘父母鼓励程度对学生上大学重要性程度的一个很好的选择。此时,你也可以将学生智商、性别、父母收入以及父母鼓励程度的数据作为输入,将学生上大学的计划作为输出。当决策树算法在扫描数据的时候,它会分析每一个输入项对结果项的影响,从而识别最能有效划分结果项的输入项。这个输入项的不同取值将整个数据集划分为两部分,在这两个数据集中,结果项的取值分布具有最大的差异性。这个过程将在每一个划分子集中重复的递归进行,直到完成整个决策树的构建。当整个训练过程结束后,你可以浏览构建好的决策树,从而识别数据中潜在的模式。

    图1.2表示了由入学计划数据集所生成的决策树。从根节点开始到叶结点的每一条路径,均各自表达了一条规则。现在,我们可以发现学生中智商高于100,并且受到父母鼓励的学生,会有94%的可能性上大学。这样我们就从数据中发现了知识。

    如图1.2所示例,数据挖掘是指通过采用算法(比如:决策树、聚类、关联规则、时间序列等)对数据集中的数据进行分析。这种分析的结果将产生一些模式,这些模式展示了一些有价值的信息。随着所采用挖掘算法的不同,这些模式有多种的表示方式,包括:树、规则、聚集、或一些数学公式的集合。在这些模式中发现的信息,可用于报表中以指导商业策略,或更重要的是进行预测。比如通过前面挖掘出的决策树模型,你可以以很高的准确度预测高校的哪些毕业生将会读大学。


图 1.1 学生数据表


图1.2 决策树

    数据挖掘能为企业提供了很多的商业价值。下面给出了一些我们对数据挖掘感兴趣理由:

  我们已经拥有大量的数据: 在过去的十几年中,硬件的价格(特别是硬盘的价格)飞快的下降。与此同时,企业的应用系统业也已经产生了大量的数据。企业越来越希望能够发现这些数据中的潜在模式,以指导他们的商业策略。

  竞争的加剧: 随着现代市场和分销渠道的采用(比如:互联网和电信),商业竞争越来越激烈。企业正面临全球的竞争,而竞争成败的关键是如何能保持现有的客户,并努力赢得新的客户。数据挖掘为进行这些因素和影响的分析提供了技术手段。

  技术已经成熟: 数据挖掘技术以前只是学术界研究的范畴,但是现在这些技术已经成熟,并逐渐的进入产业应用阶段。目前,数据挖掘的算法越来越精确,越来越高效,越来越能处理复杂的数据。

注:本文翻译自Wiley.Data.Mining.with.SQL.Server.2005
[1] 此处数值形式的数据项指的是非离散的数据项,比如年龄、收入等。


数据挖掘能解决的商务问题

    数据挖掘技术能被用于许多应用领域,解决各种各样的商务问题。下面所列出的是一些能用该技术解决的典型问题:

    流失分析:哪些客户最有可能转向购买竞争对手的产品?目前,电信、银行和保险领域正面对严峻的竞争局面。平均每个电话用户会消耗掉电信公司200美金的市场投入。每个企业都在尽可能的流住更多的客户。影响分析能帮助市场经理理解导致客户流失的原因,提高客户满意度,并最终提高客户忠诚度。

    交叉销售:哪一种商品是客户喜欢购买的?交叉销售对零售商是一个很重要的商业挑战。许多零售商,特别是在线零售商,采用这个特性来增加它们的销售额。比如,如果你到一个在线书店(如亚马逊或BarnesAndNoble.com)去购买书籍,你会注意到这些网站会给你一系列相关书籍的建议。这些建议的提出需要采用数据挖掘的分析手段。

    欺诈识别:某笔保险申请会有欺诈可能吗?保险公司每天需要处理成千上万的保险申请。这使得保险公司无法做到对每一笔申请进行有效的调查。数据挖掘技术能有效帮助保险公司识别在申请中有哪些最有可能存在欺诈行为。

    风险管理:某笔贷款应当批准吗?这是在银行经营过程中经常碰到的问题。数据挖掘技术能为银行提供每笔贷款的风险等级,帮助经理对每一笔申请做出正确的决策。
客户分类:谁是我们的客户?客户分类帮助市场经理理解不同类型的客户,并对不同的客户分类采取不同的营销手段。

    定向广告:对不同的访问者应当在网站上显示何种广告?在线零售商和门户网站希望为它们的客户提供个性化的内容。通过采用数据挖掘技术,对客户网页浏览数据和购物行为数据进行分析,得出相关的模式信息,以此来为自己的客户提供定向广告。

    销售预测:在下周这个门店能卖出多少瓶酒?这个月的库存水平该保持一个什么样的水平?数据挖掘中的预测技术能帮助解决这些与时间相关的问题。

数据挖掘的任务类型

    数据挖掘能用来解决成百上千的商务问题。根据这些问题的内在本质,我们可以将数据挖掘的任务划分为如下的几种类型。

1 分类(Classification)

    分类是数据挖掘应用中最常见的一类问题。通常,象流失分析、风险管理、定向广告等商务问题都会涉及到分类问题。

    分类指根据预测属性的取值不同,将样本划分为不同的分类。每个样本都由一系列的属性构成,其中之一称为分类属性(及预测属性)。分类任务需要寻找一个以分类属性为参数的分类函数。在前面提到的大学计划案例中,分类属性就是“College Plans”属性,该属性有两种取值:Yes和No。为了能得到数据的分类模型,需要用历史数据进行训练,历史数据中需要给出每一个样本的分类值。在数据挖掘算法中需要给定样本目标值的算法,称作指导型算法(supervised algorithms)。

    典型的分类算法包括决策树、神经元网络、Naïve Bayes。

2 聚类(Clustering)

    聚类也被称作分割。它被用于基于样本的属性,识别在样本中存在的分组。在同一分组内的样本具有更多类似的属性值。

    图1.3显示了一个包含有两个属性(年龄和收入)的客户数据集。聚类算法将这些客户划分为3个分割。分割1包含了年轻收入低的客户,分割2包含了中年收入高的客户,分割3包含了老年收入相对较低的客户。
聚类算法是一个非指导型(unsupervised)数据挖掘任务。所有的输入都同样对待,没有一个属性直接用来指导模型的构建。大多数聚类算法在构建模型时,都需要进行多次的迭代直到算法收敛。算法收敛指模型中所有分割的边界都已经趋于稳定。

 

图 1.3 聚类

3 关联规则(Association)

    关联规则是另一类常用的数据挖掘任务。关联规则也被称作购物篮分析。一个典型的基于关联规则的数据挖掘任务是,通过对销售事务表的分析,得出经常出现在同一个购物篮中的商品有哪些。从一般化角度来说,关联规则通常用于识别一些经常出现的商品集合和规则,其识别结果将用于指导交叉销售。

    对关联规则任务来说,每一个商品或(更一般化的)每一个属性/值对,被称作一个项。关联规则任务有两个目标,一个是识别一些项的集合(如商品的集合),一个是识别这些项集之间的关联规则。

    大多数关联规则算法通过多次扫描数据集,得到经常出现的一些项集。而衡量经常出现次数的门槛(及支持度),由用户在进行模型识别前指定。比如,支持度=2%,表示只有那些在购物车中出现频率高于2%的商品组合,将被识别出来进入模型。一个经常出现的项集会表达为 {Product = "Pepsi", Product="Chips", Product = "Juice"}。每一个项集都有一个大小,表示在项集中存在的项的数目。比如,前面的项集的大小为3。
    除了基于支持度识别经常发生的项集外,大多数关联规则算法同时还将识别关联规则。一个关联规则表达为:A, B => C,其中A, B, C都是经常出现的项集。同时每个规则也被赋予了一个概率值,该值在数据挖掘的术语中被称作是可信度(confidence)。在用户进行模型识别前,需要指定关联规则概率值得门槛(概率值小于该门槛的规则将被丢弃)。比如,一个典型的关联规则为:Product = "Pepsi", Product ="Chips" => Product = "Juice" (概率值80%)。这个规则所表达的意思非常直观,当一个客户已经购买了Pepsi和Chips,那么他有80%的可能性会购买juice。图1.4展示了一个产品关联规则的模型。图中每一个节点表示了一种产品,每一条边表示了产品间的关系,边的指向表示了预测的方向。比如,在Milk和Cheese间的边表示了,那些购买了Milk的客户有可能会购买Cheese。

4 回归(Regression)

    回归任务与分类任务很相似。它们之间的主要差异是预测属性(分类属性)为连续变量。回归技术在统计学领域得到了广泛的研究。线形回归(Linear regression)和Logistic回归(logistic regression)是两种最流行的方法。其它的回归技术还包括回归树(regression trees)和神经元网络(neural networks)

    回归任务可以解决很多商务问题。比如,它可以用于基于发行面值、发行方式和发行量,预测债券发行率(coupon redemption rates),或者基于温度、气压和湿度预测风速。

5 预测(Forecasting)

    预测也是一种重要的数据挖掘任务。MSFT的股值明日会是多少?下个月Pepsi的销售量会是多少?预测技术可用以帮助回答这类问题。该算法通常需要输入一个时间序列数据集(比如一个时间属性和一系列的数值属性)。时间序列数据通常含有一系列相邻的样本,这些样本之间存在顺序关系。预测技术在预测过程中,考虑了基本趋势、周期性、噪声滤波等问题的处理。最流行的时间序列处理技术是ARIMA(AutoRegressive Integrated MovingAverage model)
    图1.5中有两条曲线。实心曲线表示了微软股值变化的实际情况,虚线表示了采用ARIMA算法的预测结果。

图 1.4 产品相关性


图 1.5 时间序列

6 序列分析(Sequence Analysis)

    序列分析用于在一系列离散的序列数据间发现模式。一个序列由一系列离散的取值(或状态)构成。比如,一个DNA序列是一个由4种状态(A, G, C和T)组成的很长的序列。一个Web点击序列是由一系列的URL构成的序列。客户的购买活动也可被模型化为一个序列。比如,客户首先买了一台计算机,然后买了一个音箱,最后买了一个网络摄像机(Webcam)。其中的序列数据和时间数据都表达了相邻样本的顺序关系。它们的不同之处在于序列数据是离散的,时间数据是连续的。

    序列分析和关联规则的相似之处在于,它们所用的样本数据中,每一个样本都包含了一个项集或状态集合。其不同之处在于序列分析研究的是项集(或状态)间的转换,而关联规则模型研究的是项集之间的相关性。在序列分析模型中,先购买计算机再购买音箱,和先购买音箱再购买计算机是两种不同的序列。而在关联规则中这两种行为都表达了一个同样的项集{计算机, 音箱}。

    图1.6展示了一个Web点击的序列图。图中每一个节点表达了一个URL分类,每一条边都有一个指向,表达了在URL分类之间的转换关系。每一条转换关系都被赋予了一个数值,表示了从一种类型的URL转向另一种类型URL的概率。

    序列分析是一种相对较新的数据挖掘任务。这种类型的挖掘变得越来越重要的原因,主要来源于它所支撑的两种应用:Web日志分析和DNA分析。目前,已经出现了一些不同的序列分析技术,比如Markov链。研究人员目前正积极的在这个领域寻找新的算法。图1.6展示了从Web点击数据中挖掘出的,在不同类型URL间进行状态转换的关系图。

7 异常分析(Deviation Analysis)

    异常分析用于从样本数据中,发现与其它样本差别很大的异常数据。它也被称作特列识别,用于从以前的观测数据中识别主要的变化。异常分析能被用在很多领域。其中一个最主要的应用领域是信用卡欺诈识别。在几百万的信用卡交易事务中识别异常情况是一件有很大挑战性的工作。其它应用还包括:网络入侵识别、制造事故分析等。

    目前没有用于异常分析的标准技术,目前还处于一个被活跃研究的阶段。通常分析师会在常用算法(如:决策树、聚类或神经元网络)上进行一些改进,以满足异常分析任务的要求。为了能够产生有效的规则,分析师需要对样本集中的异常样本进行过采样(Over Sample)

图 1.6 Web浏览序列

数据挖掘技术

    虽然数据挖掘的名词还比较新,但是很多数据挖掘技术已经存在了很多年了。如果我们了解一下这些技术的起源,我们会发现这些技术都起源于如下三个领域:统计学、机器学习和数据库。

    许多上一节提到的数据挖掘任务,都集中于统计学研究领域。许多数据挖掘算法(包括回归算法、时间序列算法、决策树算法等)都是被统计学专家提出的。回归技术已经有几个世纪的研究历史了,时间序列算法也有几十年了。决策树算法是从19世纪80年代开始研究的,是较新的几种算法之一。

    数据挖掘应用是指采用自动化或半自动化的手段从数据中进行模式识别。一些机器学习算法已经被应用到数据挖掘应用中。神经元网络属于这类学习算法,它在分类(Classification)任务和回归(Regression)任务中有着非常出色的表现,特别是当属性之间的关系呈现非线性特性时。遗传算法也是一种机器学习算法。遗传算法对一组候选目标通过一个选择函数,模拟自然界的遗传和进化原理,从而逐步得到适应性更好的后代(模型)。其中,选择函数用于对候选目标进行筛选,得到下一步的子代。如此循环往复,得到最能满足要求的后代(模型)。遗传算法可以用于分类应用和聚类应用。它也可以和其它算法一起使用,比如帮助神经元网络算法找到最佳的权重。

    数据库是又一种被数据挖掘涉及到的技术。传统的统计工具假设所有的数据都能被同时调入内存。但不幸的是,在现代应用中,全部调入数据在大多数情况下不现实。比如,在一个拥有几百万条记录的数据表中发现相关性规则。而数据库则是处理海量数据的专家。正因为如此,处于实用阶段的关联规则发现算法都来自于数据库的研究成果。目前,也存在一些采用数据库技术,用于处理大数据量下的分类任务和聚合任务的算法,比如微软的聚类算法(Microsoft Clustering algorithm)。

数据流

    数据挖掘是数据仓库技术中的一个关键成员。数据挖掘技术在一个完整的商业场景中是如何发挥作用的呢?图1.7展示了这样一个典型的在业务处理的不同阶段,应用数据挖掘技术的例子。

    企业的业务系统在联机事务处理数据库中存储了大量的事务数据(Transaction Data)。这些事务数据通常通过一个提取、转换和装载过程进入数据仓库。通常情况下,数据仓库所采用的数据结构和OLAP数据库是不同的。一个典型的数据仓库数据结构是星形或雪花形的。这种结构由中心的一个事实表(Fact Table)和周围的维度表构成。在数据仓库存入数据后,就能在此基础上构建OLAP数据立方(Cubes)。

    数据挖掘技术是在企业数据流的什么地方产生作用的呢?首先也是最常见的,是在数据仓库已被清洗过的数据中应用数据挖掘技术。从数据挖掘模型中发掘出的模式,可以通过报表的方式提供给市场经理。通常在小规模的企业中没有数据仓库。如此,人们可以直接在OLTP数据表中进行数据挖掘。这种情况下,最好是将OLTP数据库中的数据表复制一份,到另外一个数据库中进行数据挖掘。

    数据挖掘也可直接用于企业业务系统,以提供预测数据。在企业业务系统中嵌入数据挖掘特征已经变的越来越流行。在一个Web交叉销售的例子中,当客户在Web上加入一种产品到购物篮后,数据挖掘中的预测查询被调用,从而基于相关性分析的结果产生针对该客户的推荐产品。

    数据挖掘也可以在OLAP数据立方上进行。数据立方是一个有着多个指标(Measure)和多个维度(Dimension)的数据库。一个大的维度可能含有几百万个数据成员,数据立方中的元素(Cell)个数是各个维度元素个数的乘积。这对于手工寻找数据中蕴含的模式是不现实的。数据挖掘技术能在一个数据立方上进行模式发现。比如,关联规则算法能用于在销售数据立方中,分析在一段时期内,某区域的客户的购物模式。我们可以用发现的购物模式预测门店销售额和销售利润。另一个例子是聚类分析。数据挖掘能将消费者通过维度的属性和指标聚分割成不同的类型。数据挖掘不仅能从数据立方中发现模式,还能对数据立方进行重新组织。比如,我们可以建立一个新的客户维度,该维度的数据基于来源于聚类分析的结果,表示每个客户属于哪一种识别出的类别,从而进行更有效的分析。

图 1.7 数据流

posted @ 2007-01-25 14:22 yiriqing 阅读(816) 评论(2) 编辑

[原创]建立Microsoft SQL Server 2005数据仓库

这几天用了一下Microsoft SQL Server 200的分析服务,贴出来给大家分享一下。
请多多指正。谢谢。

一、需求:
建立一个图书订单统计系统
1、统计各个图书馆订单数量。
2、统计各个图书馆订单的各个状态的数量占该图书馆的订单数量的百分比。
3、同时统计原始数量和储运数量
二、数据表
主要的字段
订单ID
单位号(图书馆编号)
单位(图书馆名称)
原始数量
储运数量
最新状态(该订单当前状态)
三、实现:
1、打开Visual Studio 2005 新建项目,选择Analysis Services项目,

选择数据源文件夹,新建数据源:

新建数据源向导启动:

点击上图新建出现连接配置对话框

配置方法和链接数据库的配置方法相同。

配置连接数据源的帐号,以后就是一路下一步:)

选择数据源视图文件夹,新建一个数据源视图

选择含有统计数据的表,以后就是一路下一步:)

选择多维数据集文件夹,新建多维数据集也就是一个Cube

设置事实表和维度表

选择度量值,也就是如果是SQL就是需要count,sum等要统计的数据

  取消所有选项,然后选择维度也就是SQL里面GROUP by的字段(这里我选择了“最新状态”和“单位”)

选择如上,这个不是最终结果,后面还要手工编辑。,以后就是一路下一步:)

 通过浏览打开编辑维度。

建立一个层次结构,单位在上,状态在下。

把“图书分销 订单 计数”重名名为“订单数量”

修改后如上图



切换到浏览器,拖放单位到行位置。

再拖放最新状态到行,最终效果如下。


拖放显示的数据,也就是明细


拖放完毕效果

设置显示为百分比

最终效果

打开Microsoft SQL Server 2005 的SQL Server Management Studio连接到分析服务

新建查询,查询结果如图
查询语句如下:
with
set [AllCount] as '[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].Children'
Member [图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].[合计] as 'aggregate([AllCount])'
Member [所占订单数百分比] as '[订单数量]/([订单数量],[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].[合计])',format_string='#.00%'
select {[Measures].[订单数量],[Measures].[储运数量],[Measures].[原始数量],[所占订单数百分比]} on columns,
{[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].Children} on rows
from [图书馆统计]

其实也不是很难哦

有时间下次写在 ASP .NET 2.0里面如何显示查询结果。

posted @ 2007-01-25 13:36 yiriqing 阅读(1003) 评论(1) 编辑

[原创]C#应用访问Microsoft SQL Server 2005分析服务

接上次建立的多维数据集

一、前言

1、运行环境

Windows 2003 Server Enterprise Edition with Service Pack 1

Microsoft SQL Server 2005 with Service Pack 1(包含Analysis Service)

Visual Studio 2005 Team Suite

2、本文使用Microsoft SQL Server 2000下也可使用的ADOMD .NET访问分析服务。

二、目标

查询分析服务数据转换为DataTable形式,在GridView中显示。

三、实现

通过Adomd .NET访问分析服务。通过MDX查询语言查询数据。

1、 ADO .NET一样,ADOMD .NET也主要有在线数据读取器AdomdDataReader和离线数据集(类似DataSet) CellSet

2、 访问数据的步骤为:建立连接->打开连接->建立AdomdCommand ->得到CellSet->关闭连接->CellSet转化为DataTable或将数据绑定到对应的饼图等统计图控件

3、 代码片断

l         打开连接,连接到分析服务

        public void OpenConnection()

        {

            if (_connection != null)

                if (_connection.State == ConnectionState.Closed)

                    _connection.Open();

        }

l          获得CellSet数据对象

        public CellSet ExecuteCellSet(string queryString)

        {

            OpenConnection();

 

            AdomdCommand command = _connection.CreateCommand();

 

            command.CommandText = queryString;

           

            CellSet cellSet = command.ExecuteCellSet();

           

            CloseConnection();

 

            return cellSet;

        }

l          CellSet数据对象转换为DataTable对象

        public DataTable ToDataTable(CellSet cs)

        {

            DataTable dt = new DataTable();

            DataColumn dc = new DataColumn();

            DataRow dr = null;

 

            //第一列:必有为维度描述(行头)

            dt.Columns.Add(new DataColumn("Description"));

 

            //生成数据列对象

            string name;

 

            foreach (Position p in cs.Axes[0].Positions)

            {

                dc = new DataColumn();

                name = "";

                foreach (Member m in p.Members)

                {

                    name = name + m.Caption + " ";

                }

 

                dc.ColumnName = name;

                dt.Columns.Add(dc);

            }

 

            //添加行数据

            int pos = 0;

 

            foreach (Position py in cs.Axes[1].Positions)

            {

                dr = dt.NewRow();

 

                //维度描述列数据(行头)

                name = "";

                foreach (Member m in py.Members)

                {

                    name = name + m.Caption + "\r\n";

                }

                dr[0] = name;

 

                //数据列

                for (int x = 1; x <= cs.Axes[0].Positions.Count; x++)

                {

                    dr[x] = cs[pos++].FormattedValue;

                }

 

                dt.Rows.Add(dr);

            }

 

            return dt;

        }

5、程序调用

    BaseComponent.Data.SqlAnalysisService sa

        = new SqlAnalysisService("Data Source=localhost;Catalog=LibraryStat");

 

    protected void Page_Load(object sender, EventArgs e)

    {

        StringBuilder sb=new StringBuilder();

 

        sb.Append("with ");

        sb.Append(" set [AllCount] as '[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].Children'");

        sb.Append(" Member [图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].[合计] as 'aggregate([AllCount])'");

        sb.Append(" Member [所占订单数百分比] as '[订单数量]/([订单数量],[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].[合计])',format_string='#.00%'");

        sb.Append(" select {[Measures].[订单数量],[Measures].[储运数量],[Measures].[原始数量],[所占订单数百分比]} on columns,");

        sb.Append(" {[图书分销 订单].[层次结构].[单位].[安徽大学     图书馆].Children} on rows");

        sb.Append(" from [图书馆统计]");

 

        DataTable dt = sa.GetDataTable(sb.ToString());

 

        gv.DataSource = dt;

        gv.DataBind();

    }

gv为一个GridView对象。除了查询语句不同,数据绑定是一样的,因为已经转换为DataTable了。

四、备注

命名空间:Microsoft.AnalysisServices.AdomdClient

程序集文件:Microsoft.AnalysisServices.AdomdClient.dllMicrosoft SQL Server 20059.0版;Microsoft SQL Server 20008.0版)

AdomdClient当然有AdomdServer,分析服务也包含了存储过程和CLR的存储过程。

五、后记

l         理论上本例也可以在Microsoft SQL Server 2000下运行。但是我的同事在WebForm下应用时出现错误。

l         在微软推出Microsoft SQL Server 2005之后,微软又为分析服务提供了多种访问方式。

 下次有时间讲讲如何定时从SQL 2005更新数据到分析服务。

posted @ 2007-01-25 13:31 yiriqing 阅读(439) 评论(1) 编辑

2006年7月14日 #

一步一步学Remoting之一:从简单开始

一、Remoting的优缺点?
优点:
1、能让我们进行分布式开发
2、Tcp通道的Remoting速度非常快
3、虽然是远程的,但是非常接近于本地调用对象
4、可以做到保持对象的状态
5、没有应用程序限制,可以是控制台,winform,iis,windows服务承载远程对象
缺点:
1、非标准的应用因此有平台限制
2、脱离iis的话需要有自己的安全机制

二、Remoting和Web服务的区别?
    ASP.NET Web 服务基础结构通过将 SOAP 消息映射到方法调用,为 Web 服务提供了简单的 API。通过提供一种非常简单的编程模型(基于将 SOAP 消息交换映射到方法调用),它实现了此机制。ASP.NET Web 服务的客户端不需要了解用于创建它们的平台、对象模型或编程语言。而服务也不需要了解向它们发送消息的客户端。唯一的要求是:双方都要认可正在创建和使用的 SOAP 消息的格式,该格式是由使用 WSDL 和 XML 架构 (XSD) 表示的 Web 服务合约定义来定义的。 
    . NET Remoting 为分布式对象提供了一个基础结构。它使用既灵活又可扩展的管线向远程进程提供 .NET 的完全对象语义。ASP.NET Web 服务基于消息传递提供非常简单的编程模型,而 .NET Remoting 提供较为复杂的功能,包括支持通过值或引用传递对象、回调,以及多对象激活和生命周期管理策略等。要使用 .NET Remoting,客户端需要了解所有这些详细信息,简而言之,需要使用 .NET 建立客户端。.NET Remoting 管线还支持 SOAP 消息,但必须注意这并没有改变其对客户端的要求。如果 Remoting 端点提供 .NET 专用的对象语义,不管是否通过 SOAP,客户端必须理解它们。

三、最简单的Remoting的例子
1、远程对象:
建立类库项目:RemoteObject

using System;

namespace RemoteObject
{
    
public class MyObject:MarshalByRefObject
    {
        
public int Add(int a,int b)
        {
            
return a+b;
        }
    }
}

2、服务端
建立控制台项目:RemoteServer

using System;
using System.Runtime.Remoting;

namespace RemoteServer
{
    
class MyServer
    {
        [STAThread]
        
static void Main(string[] args)
        {
            RemotingConfiguration.Configure(
"RemoteServer.exe.config");
            Console.ReadLine();
        }
    }
}


建立配置文件:app.config

<configuration>
    
<system.runtime.remoting>
        
<application name="RemoteServer">
            
<service>
                
<wellknown type="RemoteObject.MyObject,RemoteObject" objectUri="RemoteObject.MyObject"
                    mode
="Singleton" />
            
</service>
            
<channels>
                
<channel ref="tcp" port="9999"/>
            
</channels>
        
</application>
    
</system.runtime.remoting>
</configuration>


3、客户端:
建立控制台项目:RemoteClient

using System;

namespace RemoteClient
{
    
class MyClient
    {
        [STAThread]
        
static void Main(string[] args)
        {
            RemoteObject.MyObject app 
= (RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
            Console.WriteLine(app.Add(
1,2));
            Console.ReadLine();
        }
    }
}


建立配置文件:app.config

<configuration>
 
<appSettings>
 
<add key="ServiceURL" value="tcp://localhost:9999/RemoteObject.MyObject"/>
 
</appSettings>
</configuration>


4、测试
在最后编译的时候会发现编译报错:
1、找不到app.Add()
2、找不到RemoteObject
这是因为客户端RemoteClient没有添加RemoteObject的引用,编译器并不知道远程对象存在哪些成员所以报错,添加引用以后vs.net会在客户端也保存一个dll,可能大家会问这样如果对远程对象的修改是不是会很麻烦?其实不麻烦,对项目编译一次vs.net会重新复制dll。
然后直接运行客户端会出现“目标主机拒绝”的异常,也说明了通道没有打开
运行服务端再运行客户端出现“找不到程序集RemoteObject”!回头想想可以发现我们并在服务端对RemoteObject添加引用,编译的时候通过是因为这个时候并没有用到远程对象,大家可能不理解运行服务端的时候也通过?这是因为没有这个时候还没有激活远程对象。理所当然,对服务端要添加引用远程对象,毕竟我们的对象是要靠远程承载的。
现在再先后运行服务端程序和客户端程序,客户端程序显示3,测试成功。

四、结束语
我们通过一个简单的例子实现了最简单的remoting,对其实质没有做任何介绍,我想通过例子入门才是最简单的。

posted @ 2006-07-14 10:52 yiriqing 阅读(318) 评论(1) 编辑

2006年6月19日 #

       因为电子表格组件不是应用程序,所以关于它从何处获得数据如何保存它的数据的问题就显得十分关键。但是,电子表格组件针对这些问题的答案比起应用程序针对这些问题的答案要复杂的多。不过好消息是电子表格组件可以通过各种方式加载和保存数据,您可以在解决方案中灵活的使用这些方式。

 

       与应用程序不同,组件不拥有容器所使用的存储设备。将这个窗体或文档保存到永久性的设备中,以及从永久性设备中重新装载的工作,都是容器的任务。容器通常会先要求组件将它当前的状态保存在一个流或属性包(property bag)中;然后将这些数据插入到正在保存的窗体或文档当中。正因如此,任何组件在方便的装载和保存数据时,都要不同程度的受到它所在的容器的支配。不必说,并不是所有的容器都一样;一些容器要比其它的容器出色。因为认识到这一点,在电子表格组件如何装载和保存数据方面,我们设计的很灵活。实际上,您可以通过4种方式将数据装载到电子表格控件中:

n         通过交互从Excel2000中发布一个电子表格或区域。

n         Excel2000向电子表格控件中拷贝一个区域。

n         当电子表格控件位于设计器(例如Microsoft FrontPage 2000Microsoft Script EditorMicrosoft Visual InterDev,以及Microsoft Visual Basic)中时,可以直接在控件中输入数据或一组新的公式。

n         指定一个URL来装载数据,这个URL会返回一个HTML文档,其中至少包含一个HTML表格。此外,也可以从一个URL处装载以逗号分隔的文本数据(CSV)

 

Excel中发布

       Excel2000中通过交互的方式发布电子表格或区域,会提示Excel创建一个HTML文件,其中为电子表格组件包含了一个<object>的标签。Excel将所选择的电子表格或区域的内容拷贝到HTML页面文件中,作为<object>标签的一个参数,因此一旦数据被发布,它就不再引用原来的电子表格。然而,您可以方便地从Excel中重新发布内容,因为Excel会在页面文件中使用新的内容代替以前的内容,同时保留您对页面其它部分所作的修改。

 

       如果想要尝试一下从Excel2000中发布的功能,请打开您需要发布的工作薄,从文件菜单上选择另存为Web页面…”的命令。您就会看到图24所示的对话框。

 

24 选择另存为Web页面命令,来显示这个对话框

 

       选中选择这个选择项,并选择添加交互这个简化复选框。这样当您保存时,Excel就会生成一个包含了电子表格控件的页面,以及所选择内容的一个拷贝,而不是将内容保存为一个静态的HTML文件。如果需要对发布的内容进行更高级的控制,可以点击发布按钮,以显示发布为Web的对话框。

 

       当试图将某些电子表格导入HTML中时,您可能会遇到错误信息。如果源电子表格是受口令保护的(通过使用工具|保护|保护工作表命令)Excel就不会允许将电子表格或任何区域发布到web页面中。因为web页面是纯文本的文件,任何人都可以在任何文本编辑器中打开,浏览和修改它,所以如果允许这样作的话,就会破坏安全性。当电子表格的作者需要防止用户修改某部分时,电子表格就会常常被密码保护。例如,Excel中的公司开支报告就常常被密码保护,以防止员工不能修改有效性验证规则的公式。

 

       请注意,您还是可以通过使用保护功能来锁定大多数的单元,以使用户只能修改那些您指定可以更新的单元。只要您不使用口令来保护电子表格,您就可以将电子表格发布或者拷贝到电子表格组件中,而且所有的保护设置都会被保留。

 

拷贝和粘贴

       Excel 2000和电子表格组件都会读写一种扩展的HTML表格格式的区域,这种格式在HTML上扩展了能够实现专门将信息导入到Excel中的功能的附加属性和XML(扩展标记语言)代码。这就意味着您可以从Excel2000中拷贝区域,并粘贴到电子表格控件中,或者反之,对于创建电子表格,和将电子表格控件中所看到的数据拷贝到Excel中以作进一步的分析的工作来说,这都是很有用的。

 

       当拷贝粘贴区域时,您应该注意一些问题。首先,如果区域中的一个元素包含一个公式,而这个公式引用了一个不在这个区域中的单元(在另一个工组表,或在另一个工作薄中),那么Excel会只拷贝该单元的当前值,而不会拷贝公式。让我们认真考虑这个问题:如果您将一个包含了拷贝区域外单元的引用的公式拷入电子表格控件中,那么电子表格控件就无法处理这个公式,因此也就不能显示任何数据。所以任何对拷贝区域外单元的引用都会被转换为与被拷贝时所引用的值相等的一个文本值。

 

       第二,Excel电子表格中更高级的结构,例如数据透视表,只会拷贝文本数据的单元,而不会拷贝数据透视表的结构(换句话说,粘贴后的数据不能再通过透视或钻取来获得更多的信息)Office Web components是通过包含的数据透视组件来完成数据透视功能的。图表也根部不能被粘贴,因为电子表格组件不能寄宿其它的控件或浮动的图形。

 

       第三,电子表格的保护设置不仅影响内容如何被发布,还会影响内容怎样被粘贴到电子表格组件中。如果源电子表格是受密码保护的,那么区域还是能被拷贝粘贴,但是只有文本值会被粘贴到电子表格组件中。而如果电子表格是受保护的,但不是通过口令,那么区域会被正常拷贝。

      

       还要注意一件有趣的事,粘贴到电子表格组件中的格式是HTML,因此任何能够将HTML的表格拷贝到剪贴板中的应用程序也能被用来向电子表格组件中输入数据。虽然Excel2000确实在拷贝到剪贴板上的数据中嵌入了其它的信息,例如公式和一个给定单元的全精度值。但是,如果其它应用程序将不包含额外信息的HTML表格拷贝到剪贴板中,表格仍然会以附带格式的文本数据的形式粘贴到电子表格组件中。

 

直接在电子表格组件中输入

       关于这个方法,除了您会发现Excel中很多方便的创作电子表格的功能,在电子表格组件中已不存在之外,没有太多可说的。不过,您仍然可以在输入公式时通过选择单元来快速输入单元引用,通过属性工具箱来格式化信息,隐藏和显示标题栏、工具箱、列和行标头、以及表格线。在实际中,常常采用的方法是在Excel2000中建立您的电子表格,然后在完成后将内容发布和拷贝到电子表格组件中。不过如果是简单的电子表格,您可能会觉得在电子表格组件中只接输入模型更方便。

 

       之前提到过,不是所有的容器都可以在设计阶段方便的激活控件并进行交互的。只有在那些允许控件激活并能保存控件的内容的容器中,才能够实现直接在电子表格组件中录入的功能。

 

Visual BasicFrontPage中使用电子表格组件

 

       对于电子表格组件来说,Visual BasicFrontPage都是非常优秀的容器;不过,一些技巧可以帮助您,使得编辑的过程更加轻松。

 

       Visual Basic中,只要您单击电子表格组件,它就会被界面激活。这会使得很难在窗体中移动控件,因为单击和拖动操作的结果可以只是选择了一些单元的区域。不过,您可以通过单击标题栏并拖动来移动整个控件。如果标题栏不可见,可以通过设置DisplayTitleBar属性为true,来临时的显示它,然后再通过设置该属性为false来隐藏它。

 

       Visual BasicFrontPage中编辑电子表格组件时,您也应该避免使用AutoFit属性(将它设置为False)。在Visual Basic中使用AutoFit属性是危险的,因为在这种情况下,无论加载了什么内容到电子表格控件中,它都会调整大小来适应这些内容,以便不显示滚动条。如果内容比您的Visual Basic的窗体大的话,控件会立刻调整窗体边缘的大小。不过,如果您确定内容比窗体要小,并且控件绝不会获得比窗体大的内容,那么使用AutoFit还是安全的。

 

       FrontPage2000中,正常视图下AutoFit属性不会起作用;不过,预览视图下,它可以在web浏览器中正确的工作。

 

URL处装载数据

       对于将数据装载到电子表格组件中来说,URL是最奇怪,但也是最强大的机制。使用属性工具箱或编程模型,您可以让电子表格控件通过打开一个特定的URL,并加载在该URL处查找到的第一个HTML表格的方式来加载内容。CSV(以逗号间隔的文本)缺乏格式化或公式的信息,因此,您只能从CSV流中加载原始数据。数据是保留在URL所指的文件中的,电子表格控件会在每次初始化时加载这些数据。当然,这个URL可以方便地指向一个Microsoft的动态服务器页面(ASP)或者是一个CGI程序,这个页面或程序从企业数据库或其它的存储系统中动态的生成HTML表格,这样就使得您能够将动态,最新的数据加载到电子表格控件中。

 

       请注意电子表格组件使用IE中的安全机制来保证不会从除首页所在的域名之外的域名中加载数据 (根据您IE中的安全设置)。这个特性防止恶意的开发者向您发送一个包含了电子表格控件及在页面加载时就运行的脚本的web页面。如果安全特性设置的不合适,黑客就可以使用当前使用者的证书来加载敏感数据,并将数据发送至另一个地方,以供他(或她)详细查看。

 

这个特性使用客户端在IE中设置的安全设置,因此如果用户认为产生页面的站点是可信的,他们可以关闭跨域的访问警告。如果站点不在可信任的站点范围内,或者只设置了最低的安全设置,那么电子表格控件在从除首页所在的域名之外的任何域名访问一个URL之前,会警告用户。如果原始页面和加载时所在的URL位于同一个域名中,电子表格组件不会显示任何警告信息,因为这种情况被认为是安全的。

 

       我会在前面的章节中较多的讨论安全性。电子表格组件的安全机制与访问数据库时的安全机制(第五章将会更多的谈到)有一些不同:当URL指向的站点不在被信任的站点范围内时,它是不会允许用户有机会进行跨域访问的。这时电子表格控件仅仅返回一个错误值,并告诉用户该操作不被允许,然而在数据库访问的环境中,用户可以选择是否执行跨域访问。

 

       很遗憾,电子表格控件不能直接从一个二进制XLS文件中装载数据,但是它能如上面所述那样,从一个URL装载存为HTML格式的Excel文件。这就允许开发者使用Excel2000创建和维护一个电子表格模型。此外,还允许开发者在运行时直接将模型载入到电子表格控件中。

 

在同一个文件服务器上的不同共享是不同的域吗?

       OWC产品开发周期接近尾声时,一个测试人员提交了一个关于电子表格组件的bug,说即使获得数据的URL和原始的HTML页面位于同一个文件服务器上时,组件也显示了一个安全警告消息。我们很困惑,因为这似乎确实应该是一个被信任的环境,不过因为我们使用IE的安全程序来决定是否两个URL是来自于同一个域,所以我们将BUG推给了IE小组。

 

       结果发现页面的URL和数据的URL确实指向同一个文件服务器,但是是指向那个文件服务器中不同的共享的。IE小组解释说,从技术上讲这确实是两个不同的域,因为在一个机构中使用一个巨大的文件服务器来为机构中不同的组织提供服务是很普通的事,而那些有访问某个共享权限的人,并不一定有访问另一个共享的权限。

 

       因此,如果您使用文件共享访问而不是web服务器,记住就安全性而言,同一个文件服务器上不同的共享被看作是不同的域。然而,这只适用于文件共享访问,而不适用于通过HTTP访问web服务器。

 

保存数据

       就象将数据装载到电子表格组件中,并不象在Excel应用程序中操作那么简单一样,从电子表格控件中保存数据可以以各种方式发生,这使得很难解释它。

 

       大多数容器不允许用户在运行状态下保存窗体或者文档。例如,一个Visual Basic窗体在运行时是没有直接的保存机制的。IE有一些不同:在显示页面时,它提供了一个另存为的机制,但是它不允许用户改变来自web服务器的原始页面。(如果允许这样的话,那么任何黑客就都能够修改您公司的主页了!) 我们常常遇到这种情况:当我们向客户展示Office Web Components时,他们最先闪现出的假设之一就是,他们能象使用一个共享文件一样,改变电子表格控件的内容,并将它重新保存到web服务器上。但是这是不可能的,因为Web的工作的原理不允许这样做――除非IE能够给web服务器返回一个新版本的页面,而绝大部分的服务器是根本不允许这样的。

 

       为了克服这些问题,我们开发了4种从电子表格控件保存数据的方法:

      

n         使用例如FrontPage2000这样的工具打开web页面进行编辑,完成修改并将页面保存到web服务器上。IE5在文件菜单上提供了一个新的使用编辑命令,可以快速的将您浏览的页面载入到一个登记的HTML编辑器中。

 

n         使用电子表格控件的工具条上的导出到Excel”按钮,快速的将它的内容导出到Excel2000中,在Excel200中您就可以将它存储为一个Excel工作薄或者将它以原始文件的方式发布到web服务器。

 

n         将电子表格控件的内容拷贝到剪贴板上,并将它粘贴到Excel2000中。

 

n         开发人员可以使用HTMLData属性来以文本流的形式获得当前内容,并在ASP页面或CGI程序中提交它,从而将它存储在服务器上。在第八章将会演示这个技术。

posted @ 2006-06-19 16:46 yiriqing 阅读(896) 评论(0) 编辑

排序和过滤

       阅读本节时,如果您打开随书光盘Samples\Chap02目录下的SortFilterExample.htm文件。您会发现这个例子很有用。本节展示的代码和描述的特定环境来自这个文件。

       电子表格组件支持Excel中的基础的排序和过滤功能,并且通过编程模型和用户界面来提供这些功能。然而,在电子表格的用户界面中,排序和过滤的功能比起Excel有某种程度的增强。让我们来看一个例子。

 

DHTML探险

       属性工具箱是由两位OWC小组的天才程序员,Eric MattesonCesar Alvarez开发的不可思议的杰作,属性工具箱也证明了试图使用DHTML来模仿Office的用户界面风格是一件困难重重的事。早期,我们坚持认为我们应该使属性工具箱尽可能地和标准的Office用户界面相似,EricCesar也的确花费了数月的时间来改变HTMLIE浏览器,使得它能符合需求。大多数人都不相信结果竟然就在HTML中。然而,因为考虑到虽然一般人都可以很有效的使用web站点,但还是会被Office应用程序中的许多高级对话框所迷惑,所以关于使用HTML来模拟Office用户界面是否使得控件更易于使用的争论依然很突出。

 

       对于那些希望在web页面中使用Office Web Components的开发者们,我的建议是不要浪费时间企图将HTML融入到传统的微软窗体应用程序的界面中,而应该利用HTML的简洁和动态布局的优势来为您的应用程序开发一个更加自然和易于使用的界面。

 

       假设您已经开发了一个用于列出您当前的产品线的电子表格, 表格中显示了每一个产品的单价,库存数量,和定购数量,还有一个给出了销售率的计算列,用于显示潜在的价值。现在用户需要根据产品的潜在价值对产品列表进行降序排列。用户可以在电子表格用户界面中,简单的选择需要排序的区域(或者在区域中选择任意的一些单元),并点击降序工具条按钮。当按钮被点击时,一个Excel中没有的菜单在按钮下方显示出来,如图22所示。

 

 

22 使用中的电子表格组件用户界面

 

用户们在对Excel区域排序时所遇到的常见问题之一就是选择要排序的区域和选择根据哪一列进行排序。电子表格组件让用户能够方便的选择需要排序的区域,然后在用户点击工具条上的升序或降序排序按钮时,显示一个列名的列表,使用户能够选择根据哪一列进行排序。通过Range对象的Sort方法,也可以使用排序功能。它使开发人员可以在用户单击或者双击一个列标头时方便地完成一个列表的排序。

 

您可以已经注意到电子表格组件一次只能根据一列来对列表进行排序。Excel提供了一个排序对话框,可以让您同时根据最多三个关键列进行排序(例如,是否根据种类排序,然后根据发货人排序,最后根据潜在价值排序)。电子表格组件没有完成这个功能的用户界面,但是底层的引擎是支持这个功能的。您可以使用下列函数模仿多列的排序。

'--------------------------------------------------------------------------
' MultiColumnSort
' 目的: 同时根据多列对电子表格进行排序
' 传入:    电子表格中需要排序的区域的引用,
'          被排序的列的列编号的数组,
'          排序方向标志的数组(和上面的数组相同大小)
' 输出:     无 (完成排序动作)
'
Sub MultiColumnSort(Spreadsheet, Range, Columns, Directions)
    ' 启动一个撤消单位,以便可以以一个完整单位的任务进行撤消
    Spreadsheet.BeginUndo()
 
' 关闭ScreenUpdating属性,使得当我们设置过滤,排序
' 和再过滤时电子表格不会重画
    Spreadsheet.ScreenUpdating = False
 
    ' 现在递减遍历Columns数组和Directions数组,
    ' 实现我们需要的效果
    For ct = ubound(Columns) To lbound(Columns) Step -1
        ' 0 is a guess for column headings
        Range.Sort Columns(ct), Directions(ct), 0
    Next 'ct
    
    ' 打开ScreenUpdating属性开关,使电子表格重画
    Spreadsheet.ScreenUpdating = True
    
    ' End the undo unit
    Spreadsheet.EndUndo()    
    
End Sub 'MultiColumnSort()

 

       实现多列排序的技巧在于,实际上是按照被排序列所定义次序的相反次序执行排序动作。例如,如果您需要先根据类别进行排序,然后再根据发货商进行排序,函数则会首先根据发货商排序列表,然后再根据类别进行排序。当电子表格根据一个新列排序列表时,新列的每一项(新列中相同列值的一组行)中,之前根据另一列进行排序产生的次序将保持不变。我们刚才阅读的函数接收三个参数:一个需要排序的区域,一个列编号的数组,一个方向值的数组(降序或升序)。函数降序遍历这两个数组,从而实现了多列排序的效果。请注意,函数还使用了BeginUndoEndUndo方法来将所有的排序操作组合在一个撤消动作块中,这样当用户选择撤消命令时,这些排序动作就会被一起撤消。

 

       电子表格组件也支持一种新的自动过滤的用户界面。组件中的过滤函数和Excel中的过滤函数相似,但是组件用户界面中的自动过滤下拉列表有一些不同的地方。假设您需要从图22中我们刚才查看过的产品列表中,过滤掉一些产品类别来观察它会怎样影响高潜在价值的产品。开发者或用户可以打开自动过滤功能,在类别列上点击自动过滤箭头,就会看到图23所示的界面。

 

 

23.         电子表格组件的自动过滤用户界面

 

       Excel中,可以非常方便的选择单个项,然而,选择多项则需要使用高级自动筛选对话框,当您只是想要排除四五项时,这个操作就会十分费劲。而在电子表格组件中,自动过滤下拉列表为每一项都提供了一个简化复选框,在顶部还有一个显示所有项,使您能够快速切换所有项的状态(选择或不选择)

 

       机敏的读者会发现电子表格组件中的自动过滤下拉列表没有包括Excel中很有用的两个设置选项。例如,您找不到10的选项,这个选项使您能够快速过滤,得到前10(或者前n)的项。您也找不到自定义的选项,这个选项允许您完成比简单的包含或不包含的过滤复杂得多的过滤功能。很遗憾,这些更高级的功能还没有包含在电子表格组件中。不过,您可以通过调用电子表格控件的编程模型来容易得模拟这些功能。

 

    可以使用下列函数来模拟”前n位”的过滤功能:

'--------------------------------------------------------------------------
' TopNFilter
' 目的: 根据给定的列编号过滤出列表的前N项
' 输入:    电子表格和区域的引用、列编号, 
'          要过滤出的行的行数,以及标识过滤出前N行还是后N行的方向值
' Out:     无 (完成所需的过滤)
'
Sub TopNFilter(Spreadsheet, Range, ColumnNum, N, Direction)
    Set c = Spreadsheet.Constants
    Set rngData = Range
    Set af = Spreadsheet.ActiveSheet.AutoFilter
    
    ' 启动一个撤消单元,以便将来能以完整的单元进行撤消
    Spreadsheet.BeginUndo()
 
' 关闭ScreenUpdating属性开关,使得当我们在设置过滤,排序和再次应用
    '过滤时,电子表格不会重画
    Spreadsheet.ScreenUpdating = False
    
    ' 清除任何现存的过滤定义
    ClearFilters Spreadsheet
 
    ' 在给定的数据区域中根据传入的列号的列排序列表
    If LCase(Direction) = "bottom" Then
        rngData.Sort ColumnNum, c.ssAscending, c.ssNo
    Else
        rngData.Sort ColumnNum, c.ssDescending, c.ssNo
    End If
    
    '如果N+1,N+2等等的行和第N行的值相同,
'则”前N位”过滤的结果可以会包含多过N的行。
'因此循环查看N+1等行是否和第N行的值相同,
'直到查找到一个不相同的值为止。
    vNValue = rngData.Cells(N,ColumnNum).Value
    
    While rngData.Cells(N+1,ColumnNum).Value = vNValue
        N = N + 1
    Wend
    
    'N现在的值就是我们需要包括在过滤结果中的行的行数。
    Set fltr = af.Filters(ColumnNum)
    fltr.Criteria.FilterFunction = c.ssFilterFunctionInclude
    
    For ct = 1 To N
        fltr.Criteria.Add(rngData.Cells(ct,ColumnNum).Text)
    Next
    
    ' 最后引用自动过滤
    af.Apply
 
    ' 打开ScreenUpdating属性开关,使电子表格重画
    Spreadsheet.ScreenUpdating = True
    
    ' 结束撤消单元
    Spreadsheet.EndUndo()
 
End Sub 'TopNFilter()

 

       N过滤功能似乎很简单,只要先排序,然后查看最开始的N行即可。但是真正的N过滤功能可能会返回超过N行的结果,因为它实际上的意思是包括前N位的值的那些行。如果在排序后,第1011位的值相同,那么10过滤会将这些产品一起返回,因为它们都在前10位的值当中。还有,上述代码通过简单的改变排序方向(升序和降序)实现了过滤出前N位和后N位的功能。

 

    同样地,您可以通过使用下面所示的函数来模拟基于表达式的过滤功能:

'--------------------------------------------------------------------------
' ExpressionFilter
' 目的: 使用一个可以被VBScript计算的表达式,在一给定的列上过滤列表
' 输入: 指向电子表格和区域的引用,进行过滤的列号,和用来进行过滤得表达式。
' 输出:    无 (列表被过滤)
'
Sub ExpressionFilter(Spreadsheet, Range, ColumnNum, Expression)
    Dim sExp         ' 临时表达式变量
    Dim vValue       ' 临时存储变量
    
    Set c = Spreadsheet.Constants
    Set rngData = Range
    Set af = Spreadsheet.ActiveSheet.AutoFilter
 
    ' 启动一个撤销单元,以便将来可以作为一个完整单元的工作进行撤销。
    Spreadsheet.BeginUndo()
 
    ' 关闭ScreenUpdating属性开关,以便当重置过滤属性,排序和再次应用过滤    ' 时电子表格不会重画
    Spreadsheet.ScreenUpdating = False
    
    ' 清除任何现存的过滤设置
    ClearFilters Spreadsheet
 
    ' 获得指定列的过滤对象,并设置过滤功能属性为”包含”
    Set fltr = af.Filters(ColumnNum)
    fltr.Criteria.FilterFunction = c.ssFilterFunctionInclude
 
    ' 检查是否表达式包含了列值的替换符,
    ' 如果包含则设置标志
    fValueToken = cbool( _
        instr(1, Expression, g_sValueToken, vbTextCompare) > 0)
 
    '遍历各行中该列的值
    For Each cell In rngData.Columns(ColumnNum).Cells
        ' 获得当前单元的值
        vValue = cell.Value
        
        ' 如果vValue是一个字符串,为了防止其中包含了空格的情况
        ' 我们需要在它的前后加上引号
        If vartype(vValue) = vbString Then    
            vValue = """" & vValue & """"
        End If
        
        ' 组成我们需要执行的表达式,将当前行的值插入到表达式中
        ' 合适的位置处
        If fValueToken Then
            sExp = "g_fEval = cbool(" & Replace(Expression, _
                g_sValueToken, vValue, 1, -1, vbTextCompare) & ")"
       Else
            sExp = "g_fEval = cbool(" & vValue & " " & Expression & ")"
        End If
        
        ' 执行表达式
        window.execScript sExp, "VBScript"
        
        ' 全局变量g_fEval现在已经被设置成True或者False.
        ' 如果是True,该行将会被包含在过滤结果中.
        If g_fEval Then
            fltr.Criteria.Add cell.Text
        End If
    Next 'ct
 
    ' 最后执行自动过滤
    af.Apply
 
    ' 打开ScreenUpdating开关,使得电子表格可以重画
    Spreadsheet.ScreenUpdating = True
    
    ' 结束撤销单元
    Spreadsheet.EndUndo()
 
End Sub 'ExpressionFilter()

 

       上述函数使用文档对象模型(DOM)中名为execScript的方法执行表达式(DOM是为Internet Explorer中的脚本提供的编程模型)。这个方法将字符串形式的脚本代码传递给动态引擎脚本(在这个例子中,是VBScript)来计算。之后脚本代码将表达式的结果存储在一个全局变量中,以便结果能够被用来判断表达式的真假。如果表达式为真,该行将会被包含在过滤后的集合中;如果为假,该行将被排除。

 

       另外,您也可以使用电子表格组件的工作表对象中的Eval方法来计算表达式。Eval使用电子表格组件的函数库和表达式计算器,它可以代替动态脚本引擎,这就意味这在IE之外的容器中它会非常有用,另外当您希望可以让用户在表达式中使用电子表格的函数或区域的引用时它也会很有用。然而,动态脚本引擎可以提供一个强大的表达式计算器。还有,它允许您使用其它的脚本语言,例如ECMA脚本(也被称为JavaScript)

posted @ 2006-06-19 16:18 yiriqing 阅读(519) 评论(0) 编辑

编程模型要点

 

         本节中的各表是透视表组件编程模型要点的一个快速参考。当使用透视表控件编写程序时,可以参考本章了解应该使用什么属性,方法和事件。

41。绑定到数据

属性

解释

PivotTable.ConnectionString

应将这个属性设置为一个有效的连接字符串,可使透视表控件连接到数据源上。

PivotTable.CommandText

将这个属性设置为一个SQL语句,或任何提供者可接受的命令。该属性仅用于表列数据源。

PivotTable.DataMember

将这个属性设置为要使用的OLAP cube的名称,或是DataSource属性所引用的DSC中的数据集的名称。

PivotTable.DataSource

将这个属性设置为DSC的一个实例,之后就可以将它用作数据源了。即使您使用ConnectionString属性来连接数据源,DataSource属性也会返回一个DSC对象。

4-2. 调整、保存视图布局

属性或方法

解释

PivotView.Fieldsets

这个属性包含报表中可用的所有有效字段集。

PivotView.Totals

这个属性包含报表中可用的所有有效合计值。

InsertFieldset

使用这个方法在行轴、列轴或过滤轴上插入一个字段集。

PivotDataAxis.InsertTotal

在数据轴上使用这个方法可以将一个合计值插入到报表中。

PivotField.IsIncluded

将这个属性设置为False,可以将一字段集中的一个字段排除出报表。还可以使用这个属性来判断一个字段是否位于报表中。

Expanded

将这个属性设置为True,可以展开一个字段或成员,以显示它的子元素。读取这个属性的值,可以得知一个字段或成员当前是否处于被展开的状态。

PivotView.AutoLayout

使用这个方法可以为OLAP数据源清空视图,或为表列数据源将所有字段放置在细目区域中。

PivotView.AddTotal

使用这个方法根据一个细目字段创建一个新的合计值。

PivotField.SortDirection

使用这个属性来设置字段排序的方式,或获得字段当前的排序方向。

PivotField.SortOn

使用这个属性使字段的各成员根据它们的合计值排序,而不是根据它们的标题。

PivotField.SortOnScope

使用这个属性根据一个合计值对一系列成员进行排序,但只针对另一个轴上的一个范围内的成员进行排序(???)

PivotFieldset.FilterMember

使用这个属性来读取或设置过滤轴上的一个字段集中当前被选择的成员。

PivotField.FilterMembers

使用这个属性获得一个给定字段的当前过滤成员的集合

PivotField.FilterFunction

使用这个属性设置或判断FilterMembers属性所使用的过滤函数。过滤函数可以包含或排除成员,或是没有过滤动作。

PivotTable.XMLData

使用这个属性来获得当前报表的定义信息,它是一个巨大的字符串,您可以保存这个字符串,并在将来读取它,并重新设置报表。

PivotView.TotalOrientation

使用这个属性来使透视表控件将行标题显示为合计值的标题,而不是列标题。

PivotGroupAxis.DisplayEmptyMembers

使用这个属性强迫透视表控件显示完全没有数据的行或列。

4-3. 定制、格式化视图。

属性

解释

PivotTable.AutoFit

使用这个属性来关闭AutoFit特性,在一个基于表单的环境中(例如VB)尤其有用。

PivotView.TitleBar

使用这个属性来调整标题条的文字和格式。

PivotTable.DisplayToolbar,
PivotTable.DisplayFieldList,
PivotTable.DisplayPropertyToolbox

使用这些属性来控制工具条、字段列表、和属性工具箱这些元素是否可视。

PivotTotal.NumberFormat

使用这些属性来格式化合计值的数字。

PivotField.SubtotalBackColor

使用这个属性来设置小计的背景色,使得小计和那些组成小计的数字显得不同。当一个大报表中需要在视觉上区别不同级别的合计值时,SubtotalBackColor属性十分有用。

PivotField.SubtotalFont

利用这个属性,可以使小计的字体和组成小计的数字的字体不同。

PivotTable.MemberExpand

使用这个属性使透视表控件在将字段和成员添加到视图中自动展开它们。

4-4. 重要的事件。

事件

解释

QueryComplete

当透视表控件在数据源上执行了一个查询后触发该事件,通常发生该事件时应该改变报表的布局。这是一个调整报表标题,以及任何其它需要和报表同步的用户界面元素的好机会。

Click

当用户在报表上点击时触发。请使用Selection属性来判断用户点击了哪里。

DblClick

Click事件相似,但是是在用户双击时触发。常用来触发一个跳转,转到另一页以显示组成一个汇总值的细目信息,也可用来显示属性工具箱,或显示您自定义格式的用户界面。

SelectionChange

当报表中被选择的部分改变时触发。主要用于当选择了新的数值时,需要改变页面或窗体上的其它元素的情况。

PivotTableChange

在使用表列数据源时,会有多种原因触发该事件。事件的参数Reason说明了触发事件的原因,比如说,创建了一个新的汇总值,或删除了一个汇总值。

ViewChange

该事件会被频繁的触发,因此不应在该事件处理函数中处理太多的任务。视图中任何一点轻微的改动都可能触发该事件;事件参数Reason说明了具体发生了什么。请参考OWC联机帮助文件(Msowcvba.chm)中的枚举PivotViewReasonEnum,它包含了触发该事件的各种原因的完整列表。

 

 

 

 

           

         本章包括了关于透视表组件的,许多开发者的常见问题的解答,因此阅读完本章后,您应该已经比较了解这项强大技术的各项功能了。下面在第七章中将讲述更多透视表组件的内容,在该章中将研究一个完整的OLAP销售分析和报表系统的源码。

posted @ 2006-06-19 16:14 yiriqing 阅读(457) 评论(0) 编辑

高级编程技巧

 

         至此,您已经学会了如何完成一些基础的编程操作,现在让我们来讨论一些更高级的技巧。在第七章的销售分析和报表解决方案中会涉及到许多这些高级技巧,因此现在我只对这些技巧作一些简单的介绍,您可以参考第七章,其中有关于本节中所包含的代码的详细解释。

保存和恢复视图

         任何在报表系统中使用透视表组件的开发者都有可能需要向他们的用户提供如下功能:保存创建的报表视图,并在以后恢复这个报表――但报表中的数据将是最新的数据。透视表控件能够很容易的完成这个任务。在第七章的解决方案中讲述了这种技巧。

         最常见的保存和恢复视图的方法是使用控件顶级接口的XMLData属性。这是一个读/写的属性,它返回一个XML格式的庞大的字符串。别将它和用作数据源的XML流混淆了――XMLData属性返回的是当前视图的布局,格式化,过滤,排序,和等等其它的定义信息。这个定义信息的字符串对视图进行了完整的描述,但是不包含任何数值。

         如果需要保存当前视图的定义,应该读取这个属性的值,并将它保存在一个以后您可以重新提取它的地方。一个常用的方法是将这个字符串提交给一个ASP页面或CGI程序,该页面或程序会接着将这个字符串写入一个文件中,或写入数据库中并与当前用户进行关联。当用户需要再次查看报表时,程序应该从永久存储设备中提取这个字符串,并用它来设置XMLData属性。属性被设置后,透视表控件会清除所有当前显示的数据,连接到原始数据源(如果还没有连接到数据源),并执行相应的查询以重新创建报表。因此用户会在报表中看到所有最新的数据,但报表的布局和用户存储报表时完全一样。

         对于视图中那些不再有效的部分,透视表控件会简单的丢弃它们。例如,如果在保存视图后,视图中的一个字段集在数据源上被删除了,透视表控件会忽略所保存的与这个字段集有关的任何信息,而不会在视图中尝试恢复它。对于不再包含在查询返回的数据集中的汇总值或成员,也是一样的。

         可以运行随书光盘中的XMLDataProperty.htm文件来观看如何使用XMLData属性。页面顶部的按钮允许您获得XMLData属性的值,以及设置这个属性的值。此外,还可以清空透视表报表。请尝试着使用获得属性的值,清空透视表报表,以及重置属性这三个功能。

         注意,连接信息(连接字符串,以及cube名称或命令文本)也包含在XMLData属性返回的字符串中。如果在用户保存报表和重新打开它之间数据源的位置发生了变化,透视表将无法连接到数据源,并产生一个错误。如果可能会发生这种情况,您应该添加错误处理代码来进行捕捉,并在将字符串传递给透视表控件之前调整其中的连接信息。连接字符串是存储在<x:ConnectionString>标签中的,数据成员(OLAP数据源的cube的名称)是存储在<x:DataMember>标签中的,而命令文本(用于表列数据源)是存储在<x:CommandText>标签中的。可以使用VBScript或微软VBA中的InStrReplace函数轻松的查找和替换这些标签的内容。第七章中有关于XMLData属性和如何改变数据源的更多信息。

锁定视图

         报表系统经常需要发布两种类型的报表:一种是每个人都可能需要查看的标准报表,另一种是用户可以修改报表结构,并保存的特殊报表。您可能需要对标准报表进行设置,以便用户不能在报表中删除或添加字段,但仍能够进行钻取和过滤。您也可能需要禁止过滤功能,只允许在视图中进行展开和收缩的操作。透视表组件提供了一些设置选项,可以帮助您锁定视图。它还提供了一些事件,可以用来监控当前用户正在进行什么操作;但是,并不存在禁止这些操作的通用机制。

 

                   XML历险

所有这些令人惊奇的XML的功能都应该归功于另一位OWC小组的著名开发者,Kevin Grealish,他花费了无数个夜晚来研究xml解析器,名称空间,以及那些不断变化的格式和标准。当我们开发透视表组件时,关于名称空间的xml标准仍然不断的被大幅度的修改。因为我们必须要读取Excel2000发布的XML数据,所以要使我们的代码处理xml的功能与Excel保持一致,这是一件艰苦长期的工作。而如果透视表报表的数据源是表格数据,我们的代码还必须与用来装载Excel保存的XML数据流的MDAC永久提供者保持一致。最后Kevin解决了这个矛盾――所以下次在您使用XMLData属性或使用XML作为数据源时,记得要感谢Kevin呦。

 

         如果不允许用户在行轴或列轴上添加或删除字段,应该将透视表控件的AllowGrouping属性设置为False。当该属性为False时,透视表控件就会禁止用户在行轴或列轴上添加或删除字段。不过用户仍然可以将字段集添加到过滤轴上,以及将新的汇总值添加到视图中。

         如果不允许用户改变任何过滤设置,应该将AllowFiltering属性设置为False。当这个属性为false时,透视表组件允许用户打开过滤下拉列表,但是不允许用户改变当前的过滤设置――换句话说,用户可以看到当前的过滤条件,但不能改变它们。控件还会禁止用户将字段集添加到过滤轴上。

         为了保证用户不能改变应用到报表上的格式化信息,可以将AllowPropertyToolbox属性设置为False。这会使工具条上的属性工具箱按钮,以及相应上下文菜单中的菜单项不可用。这样用户甚至都不能打开属性工具箱了。但是,用户还是可以使用键盘格式化命令,例如Ctrl-B,Ctrl-I,Ctrl-U。请查看随书光盘中的LockDownView.htm例子文件,以练习这些属性的用法。

判断所选择的部分,以进行穿透钻取

         和图表组件一样,透视表组件在顶级接口中也有一个Selection属性,用来返回当前被选择的对象。而且这个属性返回的对象的类型也是变化的,因此需要使用VBScriptVBA中的TypeName函数来判断返回对象的类型。请运行随书光盘中的DeterminingSelection.htm文件以查看透视表控件可以从这个属性返回哪些不同类型的对象。

         如果知道当前被选择的是什么,就可以模拟一些有趣的功能。在这里讨论一个这样的功能:通过穿透钻取以获得详细信息。

         OLAP系统善于显示高层的数据摘要,并允许用户通过“向下钻取”来获得各层的详细信息。但是,用户最终会到达超立方体中的最底层,并常常需要“穿透钻取”的功能以获得组成最低层汇总值的下层详细信息。如果当前使用的是一个表列数据源,则透视表控件可以自动完成这个工作,因为在表列数据源的情况下,详细数据总是可用的。但是,对于OLE DB for OLAP来说,还没有一种通用方法,可用来提取合计后面的详细信息的集合。尽管如此,垂直解决方案通常都能获得足够的信息,以找出那些包含了详细数据的表列数据源,并从而能够确定应该组成一个怎样的sql语句,以获得组成这个合计值所需的所有成员的详细信息数据行。??????。

         下面取自随书光盘中DeterminingSelection.htm例子文件的代码,显示了如何从被选择的汇总值中获得所需的全部信息,以生成提取汇总值的详细信息的sql语句。

Sub PivotTable1_SelectionChange()

    ' Local variables

    Dim sel         ' Temporary selection object

    Dim sFilters    ' Current filter strings

    Dim fSet        ' Temporary fieldset reference

 

    ' Grab the current selection

    Set sel = PivotTable1.Selection

 

    ' There are many types of objects that the selection

    ' could be, depending on what was selected

    ' Examples include PivotAggregates, PivotTotals,

    ' PivotMembers, PivotFields, and PivotView

    ' You can use the TypeName function to determine the type

    ' of the object

    '

    ' If the user selected an aggregate number,

    ' the TypeName function will return "PivotAggregates"

 

    ' Set the type name label

    lblType.innerText = TypeName(sel)

 

    ' If the type is "PivotAggregates", show how to get

    ' the row and column members that define that aggregate

    ' You could of course make this a Select Case statement

    ' and handle other selection types

    If TypeName(sel) = "PivotAggregates" Then

        ' PivotAggregates could contain many items, but since

        ' this is a sample, I will just work with the first item

        Set pivotagg = sel.Item(0)

 

        ' Set the value label

        lblVal.innerText = pivotagg.Value

 

        ' Get the total caption, the row and column members,

        ' and the current filters

        lblTotal.innerText = pivotagg.Total.Caption

        lblColMems.innerText = BuildFullName(pivotagg.Cell.ColumnMember)

        lblRowMems.innerText = BuildFullName(pivotagg.Cell.RowMember)

 

        For Each fset In PivotTable1.ActiveView.FilterAxis.FieldSets

            sFilters = sFilters & fset.Caption & "=" & _

                fset.FilterMember.Caption & ", "

        Next

        lblFilters.innerText = sFilters

 

    Else

        ' Selection was something other than a PivotAggregates

        ' object. Clear the labels.

        lblVal.innerText = ""

        lblTotal.innerText = ""

        lblRowMems.innerText = ""

        lblColMems.innerText = ""

        lblFilters.innerText = ""

 

    End If 'typename(sel) = "PivotAggregates"

 

End Sub 'PivotTable1_SelectionChange()

         这段代码首先使用TypeName函数来判断被选择部分的类型;如果类型是PivotAggregates,则表明用户选择了一个汇总值。代码就会提取汇总值的数值,以及汇总值所属的合计的值。(规定了一个汇总值只能属于一个合计。)接着,代码使用辅助函数BuildFullName,来创建一个包含轴上这一层的所有成员的字符串。BuildFullName函数的代码如下:

Function BuildFullName(PivotMem)

    ' Local variables

    Dim pmTemp    ' Temporary PivotMember reference

 

    ' Start by getting the current member's name

    sFullName = PivotMem.Caption

 

    ' Set the temporary reference to the current member

    Set pmTemp = PivotMem

 

    ' Navigate up the parent hierarchy until you hit nothing

    While Not(pmTemp.ParentMember Is Nothing)

        Set pmTemp = pmTemp.ParentMember

        sFullName = pmTemp.Caption & "-" & sFullName

    Wend

 

    ' Return sFullName

    BuildFullName = sFullName

 

End Function 'BuildFullName()

         代码的关键部分是中间的While循环。前面提到过,每一个成员都有一个ParentMember属性,用于返回这个成员的父成员(如果存在的话)。如果不存在父成员,属性将返回Nothing(在其它语言中它是一个指向Null对象的指针)。循环根据在层中向上游历时所遇到的每一个成员的Caption属性来生成一个字符串。当然,您可能需要使用Name属性,或甚至是UniqueName属性(而不是Caption属性)来获得SQL语句中WHERE子句所需要的值。

显示空成员

         缺省情况下,透视表组件是不显示报表中那些每个单元格都不包含数据的行或列的。但是,有时没有数据的行或列也是有意义的,您需要显示它们。可以使用下面的代码来实现这个功能:

PivotTable1.ActiveView.RowAxis.DisplayEmptyMembers = True

PivotTable1.ActiveView.ColumnAxis.DisplayEmptyMembers = True

         DisplayEmptyMembers是行或列轴的一个属性,缺省值为False。对于OLAP数据源来说,这个设置会影响透视表组件发往数据源的MDX查询语句,因此,如果这个属性被设置为True,会传回更多的数据。而对于表列数据源来说,它也会影响用来查询临时cubeMDX语句,但是因为此时客户端包含了所有的数据,所以这个属性几乎不会影响性能――除非临时立方体非常稀疏。

显示可视合计值

         缺省情况下,透视表组件只显示“可视合计值”。这意味着报表中的小计和总计是当前报表中显示的数据的合计。如果一个成员被过滤了,那么这个成员之上的小计和总计就不会包括这个成员的值。这样,即使您过滤了某些成员,报表中显示的所有数据也会被累加起来。

         假设在行轴上有一个地理层,其中包含两个级别:州和城市。每个州的小计代表了它的所有城市的合计。现在如果过滤掉城市SeattleRedmond。华盛顿州的合计应该不变,还是变成现在仍然显示在报表中的所有城市的合计?这确实是一个争论的很激烈的问题。不过幸运的是,透视表控件支持这两种模式。

         透视表组件缺省显示可视合计值。如果需要控件显示所有成员的合计值,而不考虑成员是否可视,请使用下列代码:

PivotTable1.ActiveView.TotalAllMembers = True

         TotalAllMembers属性为True的情况下,透视表控件会在小计和总计的旁边显示一个小星号,以显示这个数值并不是仅代表可视成员的。显示星号的行为是和Excel 2000OLAP透视表报表功能中的行为相同的。如果您能够在透视表控件的下方使用HTML对星号的含义进行解释,那就更完美了。

将合计标题显示为行标题

         缺省情况下,透视表组件在报表上将合计标题显示为最内层的列标题。但有时,可能需要将这些合计标题显示为最内层的行标题。您可以使用下面这一行代码轻松的完成这个任务:

PivotTable1.ActiveView.TotalOrientation = plTotalOrientationRow

         TotalOrientation属性可以被设置为PivotViewTotalOrientationEnum常量中的一个值,该常量包含标明行方向的值,和标明列方向的值。透视表控件只能将合计的标题放置在最内层的行轴或列轴上。

自动展开

         缺省情况下,透视表组件会在将所有的字段和成员添加到视图中时,使它们保持收缩状态。用户可以沿轴以任何路径进行展开,以获得他们需要的更详细的信息。您可能需要对您的报表进行配置,使得在所有的字段和成员被添加到视图中时,会自动展开。因为这可能会产生一个巨大、难以处理的显示界面,所以应该只在自动展开功能产生的是一个大小适当的报表时采使用这个功能。

         可以使用下面这一行代码来打开自动展开功能:

PivotTable1.MemberExpand = plMemberExpandAlways

         这个属性的值取自PivotTableMemberExpandEnum枚举值,该枚举值中其它可用的值有plMemberExpandNeverplMemberExpandAutomatic(缺省值)。

         当在一个web服务器上使用透视表控件生成报表的GIF图形时(下一节我们将讨论这个功能),这个属性也很有用。设置这个属性为plMemberExpandAlways,以便成员总是被展开,这样就能保证在生成图形时,报表中的所有信息都被显示出来――这是很重要的,因为用户得到的只是报表的一个静态图形,是不能展开图形中的成员的。

Web服务器上使用透视表组件

         和其它Office Web组件一样,透视表组件也能以没有用户界面的方式被使用,您可以以内存对象的形式来创建透视表组件,然后连接到一个数据源,使用程序动态的生成一个报表,最后生成当前报表的GIF图形,或通过遍历报表视图中的元素来生成您自己的html形式的报表。在web服务器上使用透视表控件的方式,可能比书写MDX查询语句,然后使用ADO MD,最后手工格式化查询结构要更加吸引人。

         当在一个服务器上使用透视表控件时,可以使用目前为止我们所讨论的所有编程技巧。唯一一个您需要了解的新的编程元素是ExportPicture方法。这个方法几乎和第三章中讨论的图表组件的ExportPicture方法完全一样。下面的代码可以实现将当前报表输出为一个GIF图形文件:

Set fsoTemp = CreateObject("Scripting.FileSystemObject")

strFilename = fsoTemp.GetTempName

PivotTable.ExportPicture Session("strTempFilePath") & strFilename, _

    "gif", PivotTable.MaxHeight, PivotTable.MaxWidth

Response.Write "<IMG SRC='" & Session("strTempURLPath") & _

    strFilename & "'>"

Session("Pivot" & Session("cntPivotImages")) = strFilename

Session("cntPivotImages") = Session("cntPivotImages") + 1

         这段代码的关键行是对ExportPicture方法的调用。FileSystemObject(微软脚本运行时库的一个类)的一个实例获得了一个用于新的GIF图形的临时文件名,然后程序将这个文件名加上根路径,形成的结果传递给ExportPicture方法,作为第一个参数。和图表组件一样,第二个参数必须是”gif”。最后两个参数指明了高度和宽度,但这里和图表组件中的编程不同(图表组件中可以将输出的图形改变成任何尺寸),您必须使用透视表组件的MaxHeightMaxWidth属性,否则生成的报表图形可能不完整。

posted @ 2006-06-19 16:12 yiriqing 阅读(536) 评论(0) 编辑

第四章第三节   透视表组件术语

       我们设计透视表组件的目的之一,就是使表列数据源和多维数据源的用户界面和编程模型保持一致。虽然每种类型的数据源都有特殊的要求,但我们希望这两种数据源相同的部分能够在外观和使用上保持一致。

       同时,我们考虑到对于那些只是需要提取必须的数据,以便能够完成他们的工作的商业人员来说,OLAP领域中使用的那些术语太不直观了。幸运的是,Excel的透视表报表功能已经建立起一套标准的术语,而且很多用户也对其很熟悉了。因此我们沿用了这些Excel建立的术语,仅仅改动了其中的几个,因为这几个术语能够更好的表达表列数据和多维数据领域中的概念。

       我会在本书剩下部分中使用本节所介绍的术语,透视表控件的编程模型中使用的也是这些术语。我会解释每一个术语代表控件中的什么内容,以及这个术语对应表列数据源和多维数据源中的什么术语。

       414显示了一个典型的透视表报表,并突出显示了下面各节中定义的各元素。参考这个图表可以得知透视表控件中各元素的所在位置。

 

414。透视表报表中的各元素。

 

汇总值

       对于一个表列数据源来说,汇总值是字段的各明细值的一个合计函数(求总和,求总数,求最小值,求最大值)。可以使用工具条上的AutoCalc按钮或通过编程来创建汇总值,缺省情况下,因为表列数据源的源数据只是一组行记录,所以它没有汇总值。您可以使用Sum,Count,Min,Max这些合计函数来为任何字段创建多个汇总值。(只有文本属性的字段可以被计算总数。)您也可以使用透视表字段列表的上下文菜单,或者通过程序来删除任何一个汇总值。

       对于OLAP数据源来说,汇总值是代表超立方体中的一个尺寸的多个数值和合计值的集合。OLAP提供者暴露的所有尺寸在透视表控件中都可用,您可以在汇总值中随意使用这些尺寸。和使用表列数据源不同,当连接到一个OLAP数据源时,您不能创建额外的汇总字段。如果您需要一个计算出的汇总值(例如商品净利润=商品价格-商品成本),您必须在超立方体中创建一个计算尺寸。透视表控件会将这个尺寸以一个汇总值的形式暴露出来。

       您只能将汇总字段放在透视表控件的数据区域(中央区域)中,它们一般用来生成透视表报表中的所有数值。可视的汇总字段会逐一显示在透视表字段列表中,通过一个独特的图表来说明它们是汇总字段。

 

FieldsetsFields

       术语“field”常用来描述一个表列数据结果集中的列,它似乎是微软各产品所使用的一个重要术语。虽然我更愿意使用术语“column”,但是微软AccessExcel历史上一直在使用“field”。因此,Office Web组件使用fieldfieldset来描述一个表列数据源的结果列。对于一个OLAP数据源来说,field被用来描述一个级别,fieldset被用来描述一个层。一个filedset是一个共同属于一个层的一些相关联的field的集合――例如,地理这个fieldset可能包括CountryStateCity这些field

       表列数据源返回一个包含一组完全不相关的字段的结果集。换句话说,您完全无法知道哪个字段与哪些其它字段共同属于一个层。例如,如果您在一个结果集中同时包含一个Country字段和一个State字段,则没有说明这两个字段是一个层的级别的元数据。因此,当使用一个表列数据源时,每一个结果集中的字段在透视表控件中都是独立的,因而一个字段集中只包含一个字段。这个规则的例外情况是日期字段和日期/时间字段。当透视表控件在结果集中发现一个日期字段时,它自动为这个字段生成两个额外的fieldset,为这个字段提供了基于日历的时间层。一个fieldset包含字段Year,Quarter,MonthDay,而另一个包含了字段Year,WeekDay。透视表控件为您的表列结果集中的每个日期,或日期/时间字段创建这两个额外的字段集。很遗憾,在当前版本的控件中,您不能从一组结果集的字段中创建您自己的字段集层。

       在使用OLAP数据源时,透视表控件为超立方体中的每个层创建一个字段集。一些OLAP数据库还允许您为一个维定义多个层。透视表控件会将每一个层暴露为一个单独的字段集。每个字段集为OLAP层中的每个级别包含一个字段,如果存在ALL级别,控件将跳过这个级别。

 

成员

       透视表控件中的字段集包含一组成员,一个成员对应字段集中某个字段中的一个独特类别。成员在交叉报表中显示为行标头或列标头,并不会滚动,这样就总是能够看到它们。

       在使用表列数据源时,透视表组件为每个结果集字段中每个独特的值创建一个成员。如果发现了给定字段中包含NULL或空白值时,它也会创建一个Blank成员。

       在使用OLAP数据源时,透视表控件在每个层中为每个元素创建一个成员,包括可能存在的All成员。

 

行,列,过滤,和数据区(轴)

       透视表控件包含一些您可用来构建您的报表的区域。在编程模型中区域常被称为轴。OLAP数据库也使用术语“轴”来描述查询结果的一个部分。行区域是位于控件左边的区域,行标头在其中显示,您可以将一个字段拖放到这个区域,来实现将您的数据按行来分组。列区域是横穿控件顶部的区域,列表头在其中显示,您可以将一个字段拖放到这个区域,来实现按列来分组数据。您可以在这两个区域中任意数量的字段,当然,这是受您系统的可用资源限制的。

       过滤区是横穿控件顶部的条。您可以将需要过滤的字段放置在这里,一次选择一个值。例如,您可能需要查看一个产品系列、或一个国家、或一个销售人员的销售信息。报表中的数据需要被过滤,以显示仅属于被选中的成员的汇总值。在过滤去中您可以放置任意多的字段,如果您需要所有成员的汇总,可以选择“(ALL)”成员。在使用表列数据源时,您在一个过滤字段中所作的选择是在客户端上的一个本地过滤,这就意味着透视表控件在客户端上仍然保存了所有的细目数据,只是在本地对数据进行了过滤。如果您需要在服务器端对数据进行过滤,您必须在用来填充透视表报表的命令文本中使用WHERE子句。

       数据区是位于报表中央的区域,透视表控件在这里显示汇总值。将汇总值放置在这个区域中会导致透视表控件显示行成员和列成员的交叉点的数值。您可以在这个区域中防止任意多的报表汇总值。缺省情况下,数字值被显示在单独的列中(???)。

       数据区也能够显示属于一个指定汇总值的任何细目行,当然,只有当数据源是表列数据时才具有这个功能。因此OLE DB for OLAP数据源只返回合计值,而不返回这些合计值后面的细目源数据。在操作表列数据源时,透视表控件能够通过展开一个合计单元,并立刻显示相关的细目行的方式来显示隐藏的细目数据。这使得用户可以仅仅双击一个需要查看细目信息的数字,就能使透视表展开这个数字所在的单元格,并显示属于这个合计值的细目数据。

 

唯一名称,名称和标题

       在您使用透视表组件的编程模型来书写代码时,您会发现许多对象都包含与它们的身份相关的三个属性,而每个属性也确实代表了一些不同的信息。

       一个对象的UniqueName属性返回了数据源提供的唯一名称。许多OLAP数据源都包含一个创建成员,级别,维等等的唯一名称的方法,而这些名称几乎是不可读的。唯一名称一般是一个不透明的字符串,您只需读取和使用它,而不必了解它们的内部格式;不过,它们能够保证对对象进行唯一标识。这里有一个OLAP服务返回的唯一名称的例子:“[Time].[All].[1997].[Q1]”。当在一个集合中查找一个对象,或在设置一个过滤条件时,您可以使用一个对象的唯一名称。对于这些任务,使用唯一名称是最安全的,因为它防止了在在某个层的两个不同的级别中包含相同的成员名,或在同一个级别中包含相同的成员名,例如Portland,MainePortland,Oregon。这两个成员都包含相同的名称(“Portland”),但是它们的唯一名称是不一样的(("[USA].[Maine].[Portland]" vs. "[USA].[Oregon].[Portland]")。

       一个对象的Name属性比UniqueName属性要友好一些,但是它仍然要能标识一个集合中的一个对象。Name属性的初始值是对象的Caption属性中的值,但如果您为了显示的需要改变了Caption属性的值, Name属性值并不会发生变化。Name属性也可以被用于在集合中查找一个对象,或用于设置一个过滤条件,但您应该只在确信同一层中没有相同名称的多个成员的情况下使用它。

       当显示报表中的对象时使用对象的Caption属性。您不应使用它来在集合中查找一个对象,或设置一个过滤条件。例如,如果您的报表中包含一个名为Sum of ExtendedPrice的汇总字段,可能为了使名称易读,您需要将这个名称改为Sales。改变Caption属性不会影响对象的内部名称,但它会改变显示在报表上的文本。

posted @ 2006-06-19 16:03 yiriqing 阅读(358) 评论(1) 编辑

第四章第二节        透视表组件如何处理数据

 

       透视表组件最重要和最复杂的方面之一就是它是如何与各种数据源进行交互,以及它在一个会话中是如何操作数据的。本节会解释透视表控件如何与数据源通讯,以及在会话期间是如何传输和操作数据的。

       透视表控件的功能是有一点不确定的――因为它的大部分功能都依赖于它所连接到的数据源的类型。它基本上只能使用两类数据源:表列数据和多维数据。(多维数据源也可以称为OLAP数据源;本书中我会交替使用这两个术语。)我们也会讨论使用XML数据来作数据源的情况。虽然XML数据看起来与任何其它用于透视表控件的表列数据源很相似,但它还是有一些需要特别讨论的需求。

 

表列数据源

       现存任何公开数据表的OLE DB数据源都是表列数据库。一般来说,它们都是属于关系数据库引擎的领域的。不过,这个类别也能包含非关系型的数据提供者――只要它们具有某种形式的文本命令语法或者命名的表格???。

       46显示了将一个表列数据源返回的数据装载到透视表组件中后报表最初的样子。(您也可以通过运行随书光盘Chap04文件夹下的PivotTableList.htm文件来查看这个报表。)

       这个报表和在一个Excel电子表格中导入一个外部数据区域后生成的报表相似。不过,因为透视表控件结合了外部数据区域和透视表报表两者的功能,所以您现在可以在这个报表中根据任何字段来对数据分组,以及为任何字段创建一个新的合计。例如,使用“Move To Row Area”,“Move to Column Area”和“AutoCalc”工具箱按钮,您可以将这个普通的数据列表转化成如图47所示的透视表报表。

 

46。一个装载了表列数据源的数据的透视表报表。

 

47。由一个普通的数据列表创建的透视表报表。

 

       因为数据源是表列数据源,所以透视表控件可以显示任何统计值下的细目信息――这就意味着您可以展开任何数字,立刻查看到组成这个数字的各行。图48显示了访问表列数据源的一个整体结构。

 

48。访问表列数据源。

       当提取数据时,透视表控件首先连接到连接字符串中的provider属性中定义的OLE DB提供者。当在设计环境中创建报表时,您一般会在数据连接属性对话框的一个列表中选择所需的提供者。提供者是一个寄宿在客户端机器上的进程内COM组件,通常使用一种专用协议与数据服务器进行通讯(如果确实有一台服务器)。例如,SQL Server提供者使用各种协议与服务器进行通讯,最常用的协议叫做命名管道。不过,微软Jet数据库的提供者需要对MDB文件的文件访问权限,因为Jet数据库引擎不是一个客户端-服务器系统。

       当透视表控件连接到数据源后,它将它的CommandText属性中的内容传递给提供者来执行。您可以在设计阶段使用属性工具箱中的Data Source段来设置CommandText属性,或者您也可以在运行时使用代码来设置它。透视表控件使用ADORecordset对象来执行命令文本,因此任何能够传递给RecordsetOpen方法的值都可以在CommandText属性中使用。这些值一般包括SQL语句、表名、视图名或存储过程名。提供者执行后,返回一个OLE DBIRowset接口,以便客户端能够访问执行命令返回的数据。

       当操作表列数据源时,透视表控件使用ADO来将返回的数据立刻装载到名为微软游标引擎(WCE)的组件中,WCE是由微软数据访问组件(MDAC)提供的一个组件,它提供了在任何数据提供者上进行高级遍历,排序和过滤的功能。WCE将数据从数据源提供者中装载到它自己的内存缓存中,如果缓存中包含的数据超出了它所允许的内存上限,这些数据最终还是会被换页到磁盘上。(这可以防止WCE耗尽您所有可用的系统内存。)当数据被装载到WCE中后,透视表控件通过与WCE通讯来实现过滤,排序,以及遍历数据集。

       当您开始根据一个字段对结果集进行分组,或当您使用与前面所介绍方法相似的步骤来创建一个统计值时,魔术般的事情发生了。为了形成交叉表格,透视表控件使用了另一种名为“透视表服务组件”的数据管道。这个组件实际上是OLAP服务的客户端提供者,但是它也能在不需要数据源的情况下,在客户端上创建临时cube。当您根据字段进行分组或创建一个统计值时,透视表控件将一个指向数据集的引用和需要临时cube中的那个维和哪个统计值的描述信息传递给透视表服务组件。这个引擎会在微软Windows操作系统的临时文件夹下建立一个临时文件,因此如果您公司的策略是不允许web浏览器中的控件创建临时文件,那么你就要注意这个问题。

 

              命名临时cube文件

在实现这个功能时,透视表组件的明星数据开发者之一,David Worktendyke,必须设计一种命名临时cube文件的方案,以便它不会覆盖任何现存的文件或影响运行在其它应用程序中的另一个透视表控件。他提出的最终方案是使用当前进程和线程ID以及惯用的CUB扩展名来组成文件名。

因此当在使用表列数据源的透视表控件中进行分组和创建统计值时,如果您在您的临时文件夹下看到一些名字很奇怪的文件,记住它是由透视表控件创建的临时cube文件。不必担心――当控件销毁时这些文件会被自动删除。

 

当操作表列数据时,透视表控件会自动为细目数据中的每个日期或日期/时间字段生成两个时间层。一层包括年,季度,月和天的分组间隔;另一层包括年,周和天的分组间隔。(这两层都是必需的,因为周不能恰好组成一个月。)这些自动形成的层使得可以很容易的分析那些具有时间维的数据,允许您查看每一个时间间隔对应的数据摘要。

如果您计划在一个web页面上使用透视表控件,您可能还需要研究如何使用Remote Data Services(RDS)。这是MDAC中提供的另一种数据访问管道,它使用HTTP来访问数据源。当使用RDS时,客户端仅需要RDS提供者这一种提供者,它是随Office Web组件一起被安装的。RDS提供者然后就会通过web服务器和实际的数据提供者(例如,SQL Server)进行通讯,这使得原始数据源提供者只存在与服务器上。如果需要了解RDS的更多信息,请参考微软网站http://www.microsoft.com/data上关于数据访问部分的内容。

 

多维(OLAP)数据源

       您很可能非常熟悉表列数据源或关系数据源,但您可能并不了解多维(或OLAP)数据源。在我介绍透视表组件中的各元素是如何映射到多维数据库的各结构上之前,请允许我先简单介绍一下多维数据库的概念。

 

OLAP简介

       在关系数据库中,表和关系是最主要的数据结构和概念,您通过定义包含一列(或多列),主键,规则等的表来构建数据库。然后您通过指定这个表的外键(与其它表的主键相匹配)的方法来在这些表之间建立关系。指定和其它表的主键相对应的外键来在这些表之间建立关系。一旦完成了这些工作,您就可以在数据库引擎上执行SQL语句,可以根据需要使用关联,排序,限制和分组来满足客户的需求。

       在多维数据库中,主键的数据结构是一个cube,或者更准确的说,是一个hypercube(超立方体)。这个结构是一个N-维的矩阵,要可视化它有点困难。在每一维中包含的项叫做members(成员),Nmembers的交点形成一个数字。让我们来看一个例子,可以让我们觉得不那么的抽象。

       假设我们在一个超立方体中对一个公司的销售数据进行建模。在我们的例子中,我们从二维开始:产品和客户。二维结构很容易形象化,因为它的样子象一个矩形,您可能在比较两维信息时见过这种矩形,例如一个交叉表。图49显示了一个矩形可能的样子。

 

49。一个二维的数据库。

 

       请注意客户名称显示在一维中,而产品名称显示在另一维中,中央区域的数字是销售额。对于任何产品和客户的组合,都会存在一个数值,代表这个客户在这个产品上消费的金额的总和。还要注意在每一维中都有一个名为All的额外成员。这个成员代表了当前维所有成员的统计值(一般是所有成员的总和)。因此,Customers.All和某个产品的交叉点代表了这个产品的总销售额。同样,Products.All和某个客户的交叉点代表了这个客户所产生的总的消费额。两个All成员的交叉点就是所有客户,所有产品的销售总额。

       现在设想将包含销售人员姓名的第三维加入到这个矩形中。结构就会变成一个三维的立方体,在概念上类似图410所示。

       现在三个坐标――客户,产品,和销售人员――确定了立方体中的每个交叉点,或者说单元。在销售人员维中又出现了一个名为All的成员,它代表所有销售人员的销售总额。这个结构允许您从多个角度来查看数据,从而帮助您回答各种问题。因为这些数值都存储在结构中,所以多维数据库能够快速存取这些单元的任何集合。

      

       410。一个三维数据库。

       很难将四维结构可视化,但可以设想您需要对另外的数据值进行汇总。例如,您可能不但需要了解销售的物品的销售额,还需要了解销售的数量。这些多值创建了包含两个成员(销售物品的销售数量和销售金额)的第四维。这些数值在多维数据库中被称为measures(尺寸);不过大多数的数据源将尺寸看作维。图411显示了一种形象化四维数据的方法。

       在多维数据库内部仍然会在一个四维的超立方体中存储所有的数据。但是您可以采用将一个四维数据结构看作是多个三维立方体的方式来理解四维数据结构。如果您需要查看一个特定的客户,产品和销售人员交点的商品销售金额,您应该查看第一个cube。如果您需要了解同一个交叉点的产品销售数量,您应该查看第二个cube。当然您可以扩展这个例子以显示立方体中的表,以及显示cube中的的cube――但我到此为止,否则您会在企图可视化16维的空间时发疯。

 

411。一个四维数据库。

       大部分多维数据库也允许您对一个维中包含的成员进行分组,在分组中会对该成员隐式的指定了父元素和子元素。实际上,各维在它们内部都定义了一个或多个层,每一层都包含一个或多个levels(级别),每个级别又都包含一系列的成员。这模仿了大部分分类数据的自然结构――产品通常属于一个相关产品组,客户通常居住在一个国家的一个州的一个城市中,销售人员属于某个地区的某个管区,等等。例如,客户维可能包含级别All,国家,州,城市,和客户名称。国家级别中的成员集合可能是美国,加拿大和墨西哥,而州级别中的成员集合可能是华盛顿,俄勒冈,大不列颠-哥伦比亚,艾伯特,哈利斯科,韦拉克鲁斯等等。

       一个单一维中有可能包含多个层。例如,如果您拥有一个雇员维,您可能需要根据部门结构来计算差旅费,以便了解每个经理和部门主管的花费的总和,或您可能需要了解从事某项工作职能的所有雇员(例如,市场、销售、产品开发、或行政人员)的花费的总和。维中的成员是相同的(都是各个雇员),但是他们被组织到不同的层,并因此创建了不同的统计值。

       许多书籍,杂志,报告,和大量论文对多维数据库进行了深度的讨论。如果您已经购买了一个多维数据库,那么很可能您的数据库的附属文档对这些概念的描述要比我在这里介绍的详细的多。

 

透视表组件如何与OLAP数据源交互

       透视表组件与OLAP数据源通信和交互的方式,和它与表列数据源通信交互的方式相似。图412显示了对这个结构的一个整体描述的图片。

 

 

412。透视表组件和一个OLAP数据源之间的交互。

       透视表控件使用微软定义的OLE DB for OLAP标准,这个标准被许多多维数据库所支持。这个模型是OLE DB标准的一个扩展,因此透视表控件和OLAP数据提供者之间的交互方式,自然和它与表列数据提供者之间的交互方式相似。控件首先连接数据提供者,这个提供者也是一个寄宿在客户端机器上的进程内COM组件。提供者决定它如何与多维数据库进行通讯。例如,OLAP服务在客户端和服务器之间使用TCP/IPsocket连接。

       在透视表组件连接到OLAP数据源后,它可以通过透视表字段列表窗口在一个指定的超立方体中显示所有的层和尺寸。当用户将层和尺寸拖放到透视表控件中时,或者当开发者使用代码插入层和尺寸时,透视表控件在MDX(多维表达式,由OLE DB for OLAP标准定义的查询语言)中生成必需的查询请求,并在数据源上执行它们。最后数据提供者返回查询结果,透视表控件将结果显示在屏幕上。

       当操作OLAP数据源时,通过网络传输的数据量是非常小的。OLAP提供者通常只是将MDX查询字符串发送给服务器,而服务器返回的也只是您在界面上看到的单元格和成员的名称。服务器只会将统计值发送回客户端,而不是那些创建这些总值所必需的底层的细目数据。这使得透视表控件可以快速响应请求,提高了系统的可伸缩性,从而能够支持大量的并发客户端。

              我应该创建一个cube,还是只需对表列数据进行分组?

       当我向人们展示透视表组件能够对表列数据进行分组和汇总,就好象它是一个来自OLAP cube的报表时,人们常常问我,“那么为什么我还要创建一个cube?”

对这个问题的回答要分为两部分。首先,使用一个预创建,基于服务器的超立方体常常能够比通过透视表控件创建表列数据的临时立方体的方式,获得性能上的极大提高。每当您在一个表列数据中对一个新的字段进行分组时,透视表控件必须重新创建cube并重新生成所有的统计值。而一个基于服务器端的cube只会创建这些统计值一次,所有访问这个cube的客户端都会共享这些统计值。

第二,一个预创建的cube可以使用多级别来定义各层,从而建立一个在数据中进行钻取的清晰的路径。而当透视表控件对关系型数据进行分组时,只会为日期字段创建层;它无法知道例如CountryStatecity这样的字段实际上是同一层的三个级别。在一个预创建的cube中,您可以定义这些层,使得数据的用户能够方便的找到他们所需要的信息。

 

XML

       透视表组件还有一个特殊的数据源,一个返回特定格式XML数据的URL。在ADO2.1版本中,微软的数据访问小组(开发MDAC的小组)定义了一种保存OLE DB的行集合的XML格式。它们也创建了一种叫做persistence provider(持久提供者)的数据访问管道,它可以通过读写这种格式的XML数据来存取一个OLE DB的行集合。透视表控件能够使用这个提供者来将从某个URL返回的xml数据装载到微软游标引擎中,图413描述了这种情况下的结构。

 

413。使用永久提供者来将xml数据装载到WCE中。

       我会马上解释如果要使用这种方法,您必须将那种类型的连接字符串传递给透视表控件。不过,为了即将开始的讨论,还要插一段,透视表控件所需要的最重要的信息是那个可以从中获得XML数据流的URL。透视表控件将这个URL传递给持久提供者,持久提供者接着使用WindowsInternet服务请求这个URL的返回结果。然后对结果解析,并装载到WCE中,而透视表控件接着开始处理结果数据――就象它处理表列数据一样。

       这种xml数据的格式是特定的,很遗憾,这种格式相应的文档并不齐全。不过,查看这种格式最简单的方法就是使用ADO Recordset对象的Save方法将一个Recordset的内容以adPersistXML格式保存到一个文件中。如果您需要动态生成这种格式的数据――例如,在一个微软ASP页面中,您可以使用RecordsetOpen方法来测试您的输出。如果您可以将您的XML数据装载到一个ADO Recordset对象中,那么它也会成功的被装载到透视表控件中,因为控件使用的是和ADO Recordset对象同样的机制。可以查看第6章介绍的解决方案的源代码,其中有在ASP页面中生成XML数据的例子。

posted @ 2006-06-19 16:03 yiriqing 阅读(344) 评论(0) 编辑

仅列出标题  下一页