本文介绍了创建和自定义 Microsoft® Visual Studio® Team Foundation Server (TFS) 报表的重要概念和分步说明。
下载
下载本文中使用的TFS 示例报表 。
导言
如果您用过 Microsoft® Visual Studio® Team Foundation Server (TFS),就可能已经发现某些报表需要进行自定义。最近,我自愿为 Microsoft 的 Microsoft Solutions Framework (MSF) 团队修订了一些标准报表,必须承认我并没有意识到这项工作的难度。很快,我发现这项自愿进行的工作远远出乎我的意料。处理报表令人生畏,因为您可能并不熟悉它需要的各种技术。本文介绍了快速熟悉这些技术需要了解的重要概念。
TFS 中的报表是在 Microsoft SQL Server Reporting Services 和 Microsoft SQL Server Analysis Services 的基础上生成的。除了 Visual Studio,您还需要一些其他工具,如 Business Intelligence Development Studio。
TFS 数据库
我们首先看一下 Team Foundation Server (TFS) 如何存储您可能要在报表中使用的信息。TFS 数据库可以分为三个存储,其中数据始终从左到右流动,如图 1 所示。每个存储针对一个特定的使用类型进行优化,下文会对此进行介绍。
图 1
TFS 数据流;最左侧是最新的联机数据,其右侧是历史数据
TFS 的 OLTP 数据库
TFS 首先进行联机事务处理 (OLTP) 存储,该存储包含所有“实时”数据。OLTP 存储包含多个数据库。每个 TFS 工具都有自己的数据库或数据库集,如工作项跟踪、源代码控制以及生成。除非您正在编写需要使用它自己的数据库的自定义工具,否则可能不需要了解这些单个数据库,而只需知道存在包含多个数据库的联机存储即可。
此存储旨在加快事务处理速度和保证数据完整性。此外,大量数据存储在规范化的数据表中,所以同一信息不会在多个位置同时存储,这有助于提高数据完整性和性能。但是,规范化意味着一个报表所需的信息可能分布在多个数据表中。如果您曾尝试整理 TFS 使用的 OLTP 数据表,就会理解我的意思 — 浏览这些数据库并不容易。各种架构难以理解,并且很难弄清要联接哪些数据表。
查询性能也是一个问题。联接的数据表越多,查询运行的速度就越慢。此外,如果您制作报表和进行分析的过程制约了更新工作项的速度,您可以想象到联机用户会很不满意。
TFS 关系仓库
幸运的是,有一种简便方法可以解决这些问题,即使用专门为查询(而不是事务处理)设计的数据仓库。TFS 使用名为 TFSWarehouse 的关系仓库,该仓库的架构更易于理解,因为它针对查询和制表(而不是事务处理)进行了优化。此外,该数据库可以放在一个完全不同的服务器上,以免查询影响 TFS 的速度。
数据通过 TFS 仓库适配器传输到此仓库中。每个工具都有一个适配器,如工作项跟踪、生成和源代码控制。这些适配器定期运行(默认设置为每小时运行一次)以更新仓库中的数据。
有关详细信息,请参阅 MSDN 上的了解数据仓库的体系结构 。
OLAP 多维数据集
最后一个数据库不是关系数据库,而是联机分析处理 (OLAP) 数据库。您可以通过 Microsoft SQL Server Analysis Services 访问此数据库,该数据库甚至拥有自己的查询语言(称为 MDX)。此类型的数据库非常适用于分析历史数据和计算值。在讨论某些您可能不熟悉的概念后,我们会回来对此进行详细介绍。
维度、事实、星型和多维数据集
该仓库使用星型架构进行组织,从而更容易回答在分析数据时遇到的常见问题。在深入了解星型架构之前,我们先谈论一下您可能要询问的问题类型以及您希望查询如何运行,这会有所帮助。以下是您可能要询问的一些问题示例:
- 项目的特定区域还剩下多少个工时?
- 上周发现和关闭了多少个错误?
- 我是否可以查看显示上个月剩余工作变化情况的每日走势图?
这些只是项目经理要查看的信息类型的一些示例,有助于他们了解项目的运行状况和进度。
类似这些内容的问题可以分为维度和事实,这是星型架构理念的核心概念,您很快就会看到。事实指一些值(如小时数),维度指用于控制所见内容的参数。您可能经常需要合计事实,如选择查看的每个区域的剩余小时总数。
如果查看这些问题,就会注意到它们各自都有几个与之相关的不同维度。例如,第一个问题使用“区域”维度、“项目”和“工作项类型”(后者并不直接用于问题,但是问题通常会询问任务工作项中的剩余小时数)。
您可以重新描述第一个问题,使其明确表示使用的维度和事实。类似以下内容:
针对“区域”维度中特定的值列表、“项目”维度中特定的值以及“工作项类型”维度中的值任务,显示“剩余小时”事实的总和。
图 2 显示了“工作项历史记录”的事实和维度视图。
图 2
“工作项历史记录”的星型子集
图 2 中的示例在实际数据表中包含更多的维度和事实,不过该图说明了它为什么称为星型架构。
在图 2 所示的示例中,“剩余工作”和“已完成工作”是两项事实,而“团队项目”、“日期”、“迭代”和“区域”都是维度。
TFS 的关系仓库使用不同的数据表来存储不同的事实集合。例如,图 2 中的“工作项历史记录”是许多事实用来保存有关某工作项的历史信息的数据表的名称。此外,每个维度还有一个数据表。事实数据表具有将其链接到不同维度的外键。
每当 OLTP 存储中的工作项发生更改时,都会向“工作项历史记录”数据表中添加行。这些行会链接到每个维度表中的特定值,从而允许您使用这些不同的维度分解数据。
TFSWarehouse 关系仓库实际包含多个星型;它事实上还使用了基于星型的另一种架构,称为雪花型。在此我们同样不再介绍其细节,因为您可以看到,我们不需要深入了解其详细级别。
表 1 列出了 TFS 中每个星型的中心位置的不同事实数据表。
表 1:TFS 关系仓库中可用的事实数据表
事实数据表 | 事实数据表 |
---|---|
生成变更集 | 负载测试摘要 |
生成覆盖率 | 负载测试事务 |
生成详细信息 | 运行覆盖率 |
生成项目 | 测试结果 |
代码改动 | 相关的当前工作项 |
当前工作项 | 带有结果的工作项 |
负载测试计数器 | 工作项变更集 |
负载测试详细信息 | 工作项历史记录 |
负载测试页摘要 |
表 2 列出了所有维度(在数据表中实现)。
表 2:TFSWarehouse 关系数据库中可用的维度
维度 | 维度 | 维度 |
---|---|---|
区域 | 迭代 | 结果 |
程序集 | 负载测试计数器维度 | 运行 |
生成 | 负载测试页摘要维度 | 运行结果 |
生成风格 | 负载测试方案 | 团队项目 |
生成质量 | 负载测试事务维度 | 测试类别 |
生成状态 | 计算机 | 现在 |
变更集 | 输出 | 工具项目显示 Url |
日期 | 人员 | 工作项 |
文件 | 平台 |
并非所有事实数据表都使用所有的维度。您可以使用 Microsoft SQL Server Management Studio 研究这些关系;您会发现事实数据表中包含哪些事实,以及事实数据表使用哪些维度。图 3 显示了在 Management Studio 中看到的一个示例维度。您会注意到它包含有关日期的多个冗余信息片段。这些额外的信息片段用于层次结构(本文稍后会进行介绍),并帮助分解查询中的数据。
图 3
在 Management Studio 中看到的“日期”维度
图 4 显示了事实数据表的示例;在本例中是“工作项历史记录”。您可以看到它包含外键;每个外键代表一个维度。其他列代表将存储在此数据表中的事实。因此,每行包含一组事实并且连接到不同维度表中的特定值。
图 4
在 Management Studio 中看到的“工作项历史记录”事实数据表
TFS OLAP 多维数据集
关系仓库中的星型架构肯定很有用,但它并不能完全满足您的需要。许多报表都会合计值;根据您决定用于筛选的维度以及对您返回的数据的分类,合计值的方法会有所不同(这在 Visual Studio 中开始使用查询时更有意义)。由于事实数据表中可以包含上百万的行,合计的速度可能很快下降。在这种情况下,使用“多维数据集”可以对性能和能够检索的数据类型和格式产生极大的影响。图 5 概述了 TFS 使用的不同存储的优点。在此图中,ETL 指“提取”、“转换”和“加载”,即从 OLTP 架构转换为仓库的星型架构所使用的过程。此外,该过程还涉及 Microsoft SQL Analysis Services 对关系仓库中的数据执行的额外处理,如预先计算合计值。
图 5
TFSWarehouse 存储的特征
维度层次结构
当您向分层维度添加其他元素时,多维数据集的优点更加清晰。我们先看一个常见示例。如果要查看日期,您可以按照不同的方式对其进行分组,如按年、月或日。实际上,如果您再查看一下图 3,会注意到该数据表包含用于显示每个分组和其他内容的一列。这些分组具有清晰的层次结构。1 年包含 12 个月,每个月包含多天,如图 6 所示。
图 6
TFS 中“年月日”层次结构的示例
此类层次结构非常适用于合计值,因为它可以回答其他结构难以回答的问题。例如,与上一月的结果相比,本月发现的错误和关闭的错误的百分比如何?换句话说,如果现在是 4 月,我们希望将本月的值与 1 月的值进行比较。令人非常惊讶的是,只要在多维数据集中定义了层次结构,SQL Server Analysis Services (SSAS) 就会像本例中一样,在不同层次结构级别预先计算一系列合计值。这也使得在此层次结构中的特定级别移动到上一个和下一个节点变得非常容易。例如,2007 年 1 月的前面是 2006 年 12 月,这非常明显。但使用标准的 SQL 却很难编写执行此类移动的查询,而使用 SSAS 及其 MDX 查询语言就很容易实现。
图 7 显示了我们稍后会使用的查询生成器中显示的“日期”层次结构的示例。该图显示了三种不同的层次结构。第一种实际上不是树状结构,因为它只是日期的平面列表。第二种依次按年、月、日进行组织,如图 6 所示。最后一种与之非常相似,不过它按一年中的周数(而不是月)进行组织,因此是从 1 到 52 周。
图 7
TFS 中的多维数据集有三种不同的“日期”层次结构(最右侧节点旁边显示的每个小点代表它在层次结构中的级别;在具有两个点的节点下是三个点 [按三角形排列]
设置计算机
现在,介绍理论已经够多了。在本节中,我们将设置您的计算机,以便开始使用关系仓库和多维数据集中的数据。
安装所需的工具
首先,您需要确保计算机上安装了所有需要的软件。本文假设您已经在可访问的服务器上完全安装了 TFS,所以本节只讲述安装与 TFS 的数据存储进行通信和创建报表所需的工具。您需要下列工具:
- Visual Studio 2005 Professional Edition 或 Visual Studio 2005 Team System
- SQL Server 客户端工具:
- 管理工具(可选)
- Business Intelligence Development Studio
- SQL Server 联机丛书(可选)
- Visual Studio 2005 SP1
- 针对 Windows Vista 的 Visual Studio 2005 SP1 更新(如果使用的是 Windows Vista 操作系统)
- SQL Server 2005 SP2
Business Intelligence Development Studio(属于 SQL Server 客户端工具的一部分)将所需的工具安装到 Visual Studio 中以创建和自定义 TFS 报表。
注意 |
---|
如果要在运行 Microsoft Windows Vista 操作系统的计算机上运行 Visual Studio 2005,您需要以管理员身份启动 Visual Studio 才能使用报表。 |
创建报表服务器项目
安装所有工具后,您将在 Visual Studio 的“新建项目”对话框中的“商业智能项目”下看到一组新的项目类型,如图 8 中所示。指定项目名称并选择位置后,请单击“模板”下的“报表服务器项目”,然后单击“确定”。
图 8
正确安装所有工具后,“商业智能项目”项目类型将出现在 Visual Studio 中
新项目应该只包含两个空文件夹,如图 9 所示。
图 9
新的空报表项目应该与此内容类似
创建数据源
下一步是添加两个数据源:一个连接到关系仓库,一个连接到多维数据集。要添加关系数据源,请执行以下操作:
- 右键单击“共享数据源”文件夹,然后单击“添加新数据源”。
- 在“常规”选项卡上的“名称”文本框中,键入TfsReportDS。TFS 中的许多报表都需要此数据源名称,所以当您为 TFS 编写报表时该名称尤其重要。
- 在“类型”组合框中,单击 Microsoft SQL Server。
- 创建连接字符串以连接到承载数据仓库的 SQL Server 实例。单击“编辑”按钮,然后在字段中输入适当的信息即可。您需要选择 TFSWarehouse 数据库,并确保管理员已授予您访问该数据库的权限。
- 单击“确定”。
接下来,您需要创建连接到多维数据集的数据源。为此,请执行以下操作:
- 右键单击“共享数据源”文件夹,然后单击“添加新数据源”。
- 在“常规”选项卡上的“名称”文本框中,键入TfsOlapReportDS 。TFS 中的许多报表都需要此数据源名称,所以当您为 TFS 编写报表时该名称尤其重要。
- 在“类型”组合框中,单击“Microsoft SQL Server Analysis Services”。
- 创建连接字符串以连接到承载数据仓库的 SQL Server 实例。单击“编辑”按钮,然后在字段中输入适当的信息即可。您需要选择TFSWarehouse 数据库,并确保管理员已授予您访问该数据库的权限。
- 单击“确定”。
现在,您的项目应该包含两个数据源,如图 10 所示。
图 10
设置两个数据源后,您将在“解决方案资源管理器”中看到如下内容
添加报表
如果按照常用方式添加报表,将出现引导您完成创建报表的向导。本文将忽略此向导,所以我们可以直接跳到使用查询。要添加报表,请执行以下操作:
- 右键单击“报表”文件夹,指向“添加”,然后单击“新建项目”。
- 在“添加新项”对话框中,单击“报表”,指定所需的名称,然后单击“添加”。本文中的示例使用名称“测试报表”。
现在,您应该在“解决方案资源管理器”中的“报表”文件夹下看到了报表。打开报表时(它通常在您添加新报表时自动打开),您将看到一个对话框,其中包含三个选项卡:“数据”、“布局”和“预览”。
生成简单查询
在本节中,我们将使用报表的“数据”选项卡。要创建连接到多维数据集的新数据集,请执行以下操作:
- 在“数据集”组合框中,单击“新建数据集”。这将打开“数据集”对话框。
- 在“名称”文本框中,键入一个名称。本文中的示例使用名称 dsTest。
- 在“数据源”组合框中,单击“TfsOlapReportDS(共享)”。此操作会将该查询连接到多维数据集而不是关系仓库。
- 单击“确定”以创建此数据集。
此时,将看到与您以前见过的其他查询窗口完全不同的一个查询窗口,如图 11 所示。
图 11
创建连接到多维数据集的新数据集后,会显示此编辑器;如果没有使用企业版的 SQL Server,左侧列表将更长
此窗口包含四个主要区域:元数据、计算成员、查询结果和维度/筛选器。目前,我们将忽略“计算成员”区域,但是您稍后会看到,该区域在对返回的数据进行计算方面非常有用。
在创建查询之前,您在图 11 中看到的“元数据”区域会同时显示度量值、KPI(关键绩效指标,在本文中将忽略)以及维度。现在,维度列表仅限于名为“生成”的子集。SQL Server 的企业版支持“透视”,即允许将维度视图限制为与您要生成的查询类型相关的子集。标准版仅支持一个名为 Team System 的透视。如果您看到“元数据”选项卡上方出现“生成”,请单击“生成”右侧的省略按钮 (...),然后单击“多维数据集”对话框中的Team System。
现在,您应该会看到非常长的维度列表,如图 12 所示。
图 12
这是查看整个多维数据集(而不是透视)时看到的内容
当前,查询结果窗口不显示任何内容。通过将度量值从“元数据”区域拖动到查询结果区域,可以在该窗口中显示内容。为此,请执行以下操作:
- 展开树的“度量值”分支。
- 打开“当前工作项”文件夹。
- 将“当前工作项计数”度量值拖动到查询结果区域,如图 13 所示。
图 13
将度量值拖动到结果区域将显示合计值
现在,您会看到类似图 13 的内容,但是您在“当前工作项计数”下看到的值几乎完全不同。此值是多维数据集中工作项的总数。您还可能会注意到此数值几乎立即显示,这是因为多维数据集会预先计算一系列合计值。
添加一些维度
拥有工作项的总数很有意思,不过并不是很有用。假设您希望看到特定项目中包含多少工作项。为此,请执行以下操作:
- 将“团队项目”维度拖动到查询结果区域上方的维度区域中。
- 单击“筛选器表达式”单元格,然后单击与组合框类似的框中的下拉箭头。
- 选择要查看的项目,如图 14 所示。
图 14
仅针对 Microsoft 的“模式和实践”组中“企业库”项目的筛选结果
您会注意到现在显示的数值较低(假设您的 TFS 服务器上正在运行多个项目),因为现在只显示一个项目中的工作项数。
显示多行
到目前为止,我们看到查询只返回了单个数值。这没有太大用处。毕竟,单个数值很难用图表示并让它看起来有趣。下一步是添加维度属性作为结果区域的新列。为此,请执行以下操作:
- 在“元数据”区域中,向下滚动到“工作项”维度,然后将其展开。
- 将Work Item.Work Item Type 属性拖动到结果区域中。在释放鼠标按钮前,请注意有一条红线会显示将在结果中放置该列的位置。在本例中,它将该列置于当前列的左侧。
图 15
现在,结果会显示项目中每个类型有多少工作项
该结果是在项目中使用的工作项类型的列表,以及每个类型的数量。示例结果如图 15 所示,但是您的结果可能看起来完全不同。
生成错误率报表
根据以上基本原理,我们将创建一个真正的报表。我们将使用我为 Microsoft 的 MSF 组创建的“错误率”报表的更新版本作为示例。但是,我们不会创建完整的报表,而只需让您了解如何添加所有详细信息或了解现有报表中的内容即可。图 16 显示了完整报表的样子。您可以看到,有六个不同的参数控制着报表显示的内容。对于我们将在此处生成的报表,将只添加一个参数以便了解它的执行方式。
图 16
在 Visual Studio 中运行时,MSF 自带的较新的“错误率”报表与此类似;有六个参数控制着显示内容
让我们想一下创建此图表需要哪些数据类型。我们将用图表示历史记录;在本例中,我们用图表示一个月内的数据。该数据是报表每天处于各种状态的错误数。为使内容更加有趣,此图表还使用了一个滚动平均值使线条更为平滑。粗线使用 7 天的移动平均值,细线使用 8 周的移动平均值。
最初,我们编写一个返回没有经过平滑处理的原始值的查询。对于此查询,我们需要考虑要使用哪些度量值和维度。我们希望在要使用的日期范围内的每一天都返回结果,所以我们希望包括 Date.Date 维度。回忆一下图 7,其中有多个针对“日期”的维度层次结构,但是我们除了日期外不需要其他内容。
我们实际上希望开始一个新的查询。如果需要,您可以删除现有的查询(为此,请单击“删除所选的数据集”工具栏按钮 [就是上面有红色 X 的按钮])。要创建新的查询,请执行以下操作:
- 在任意事件中,使用 TfsOlapReportDS 创建一个名为dsBugRates 的新数据集。
- 确保您正在查看Team System 透视而不是“生成”。
- 在“元数据”树中,展开“度量值”分支。
- 打开“工作项历史记录”文件夹(因为我们需要历史数据而不是当前数据)。
- 将“累计计数”度量值拖动到查询结果区域。
- 将“团队项目”维度拖动到维度/筛选器区域,然后选择要查看的项目。
- 最后,将 Date.Date维度拖动到查询结果区域。要找到此维度,请展开“元数据”区域中的“日期”维度。
此时,您会看到结果区域中的许多行,每行对应一个日期。假设您使用的项目包含一些历史记录,该结果集将很长。添加更多维度将导致结果集变得更大,因此在添加更多维度之前最好添加一些筛选器。为此,请执行以下操作:
- 将 Date.Date 维度拖动到维度/筛选器区域,如图 17 所示。
- 单击“运算符”列以指定 Date.Date,然后单击组合框中的“范围(包含)”。
- 单击“筛选器表达式”列以指定Date.Date,然后在左侧组合框中单击起始日期,在右侧组合框中单击结束日期。图 17 中的示例显示了 2007 年 1 月的日期范围。
图 17
基于日期范围进行筛选
现在,您只能看到一个月的工作项。但此时我们要查看所有工作项;对于名为“错误率”的报表,我们实际上只查看“错误”工作项类型。您可以添加另一筛选器来完成此操作。为此,请执行以下操作:
- 在“元数据”区域中,展开“工作项”维度。
- 将 Work Item.Work Item Type 维度拖动到筛选区域中。
- 在“筛选器表达式”单元格中单击,然后选中“错误”工作项类型的复选框(或者在项目中使用的任何名称)。如果您有多个用于错误的工作项类型,可以选择多个对应的复选框。
添加此筛选器后,您会注意到查询结果窗口将更新,并且数值也很可能不同。实际上,假如项目中存在其他类型的工作项,它们会低于原来的值。
考虑要检索的内容
到目前为止,结果显示了项目中每天出现的错误总数。由于 TFS 无法删除工作项,此数值将随着时间逐渐增大,因此,针对此信息进行绘图实际上用处并不大。
此时,最好后退一步,思考一下您要尝试使用报表实现什么目标。查看错误报表时,您可能希望了解总数随着时间变化的情况,这是目前为止我们实现的查询。或者,您要查看更改率随时间的变化。后者意味着您要查看每天报表中增加了多少新错误、解决了多少错误以及关闭了多少错误等。由于这称为“错误率”报表,因此“比率”一词表示我们对趋势感兴趣;换句话说,我们希望查看更改为每种状态的错误数,而不是某一天处于每种状态的错误总数。
幸运的是,多维数据集具有一个名为“状态更改计数”的度量值,我们可用它来获取此信息。为此,请执行以下操作:
- 单击并将“累计计数”标题拖出结果区域。此操作会将该列从查询中删除。由于我们不要求使用任何度量值,因此,您将看到结果区域为空。
- 在“元数据”区域的“度量值”分支中,打开“工作项历史记录”文件夹,然后将“状态更改计数”度量值拖动到结果区域中。
此时,您将看到一组截然不同的数字。您可能会看到整个月内数字上下波动。您还会发现缺少某些日期(如果当天项目中没有工作项更改状态,就会出现此情况)。
添加状态维度
现在,让我们将每天的状态更改分为不同的状态。“错误”工作项可以拥有多种状态。现在支持“活动”状态、“已解决”状态和“已关闭”状态,但您的工作项可能拥有更多(或不同的)状态。这些状态在“工作项”中定义,因此,我们将拖动状态维度并将其作为一列添加到结果中。为此,请执行以下操作:
- 在“元数据”区域中,展开“工作项”维度。
- 将 Work Item.State 维度拖动到结果区域中。您会发现,红色竖线既允许您将其放在日期的左侧,也允许放在右侧。将其放在右侧,以显示按照日期、状态进行分组的结果,如图 18 所示。
图 18
运行具有“日期”和“状态”维度的查询后返回的部分结果
这是创建图形报表目前需要的所有数据。
添加图表
到目前为止,您执行的所有操作都是在 Visual Studio“报表设计器”窗口的“数据”选项卡中进行的。您还可以使用此窗口中的另外两个选项卡编辑和预览报表。本节将图表添加到报表中并对其进行设置,以便该图表显示查询中的结果。为此,请执行以下操作:
- 确保可以看到 Visual Studio 中的“工具箱”面板。
- 在“工具箱”面板中,单击“图表”项。
- 单击并拖动到“布局”区域中以创建新的图表。
- 拖动图表的右下角将其调整为所需的大小。
此时,您将看到类似于图 19 的内容。如图所示,选中图表时,将显示三个拖放区域,分别对应于数据、系列和类别字段。本文稍后会对此进行介绍。
图 19
如果选择的空图表包含多个拖放区域,您可以使用这些区域来添加要用于绘图的数据
添加数据之前,请更改图表类型,以便显示折线图而不是条形图。为此,请执行以下操作:
- 右键单击该图表,依次指向“图表类型”、“折线图”,然后单击“简单折线图”。
现在,应该为图表填充数据以进行绘图。为此,请执行以下操作:
- 确保“数据集”面板在 Visual Studio 中处于开启状态(为此,请单击“视图”菜单上的“数据集”)。
- 在“报表数据集”分支下,展开 dsBugRates 数据集。
- 在“布局”选项卡上,双击该图表。将显示三个数据拖放区域。
- 将 State_Change_Count 列从“数据集”面板拖动到图表的“将数据字段拖至此处”拖放区域。
- 将“状态”列拖动到“将系列字段拖至此处”拖放区域。
- 将“日期”列拖动到“将分类字段拖至此处”拖放区域。
- 在“报表设计器”窗口的顶部,单击“预览”选项卡。
此时,您将看到在 Visual Studio 中运行并使用 TFS 服务器中的数据的实际报表,如图 20 所示。
图 20
按日期显示状态更改计数的报表(缺少部分日期)
请仔细查看此图表。是否发现了某些错误?是否发现缺少某些内容?请查看底部的日期。该图表缺少部分日期。为什么?
我们创建的查询仅显示度量结果不为空的行。某些日期没有更改状态的工作项。例如,您会发现缺少星期六(因为我的测试人员在印度,其时区与我们截然不同,所以显示星期日)。如何才能显示全部日期呢?可以添加一列(如累计计数),使之不为空。为此,请执行以下操作:
- 在“元数据”区域中,展开“度量值”节点。
- 展开“工作项历史记录”文件夹。
- 将“累计计数”度量值拖动到结果区域中(此列的顺序并不重要,但添加到最右侧比较容易)。
现在,结果将包括当天所有状态更改值为空的日期行。如果再次单击“预览”选项卡,报表将更新以显示所选范围的所有日期。
添加计算成员
上文提到,我们最终将改用移动平均值以使线条平滑。为此,我们可以在使用多维数据集中的值计算出的结果中新建一列。
位于“数据”选项卡底部的是名为“计算成员”的区域。我们将在此处定义移动平均值。由于语法实际上使用专用于多维数据集的查询语言的一个代码段,所以看起来有些怪异。此查询语言名为 MDX(代表 Multi-Dimensional eXpression),是利用维度和度量值知识专门为使用多维数据集而设计的。若要定义移动平均值,请执行以下操作:
- 在“计算成员”区域中右键单击,然后单击“新建计算成员”。将打开“计算成员生成器”对话框。
- 在“名称”文本框中,键入“移动平均值”。
- 在“表达式”文本框中输入以下内容:
Avg( [Date].[Date].CurrentMember.Lag(6):[Date].[Date].CurrentMember, [Measures].[State Change Count] )
- 单击“检查”按钮以确保表达式有效。
- 单击“确定”以完成此计算成员的创建过程。
- 将“移动平均值”成员拖动到结果区域中。
- 切换到“布局”选项卡,然后将 Rolling_Average 列从 dsBugRates 数据集拖动到图表的顶部拖放区域。
- 在顶部拖放区域中,右键单击“状态更改计数”,然后单击“删除”将旧列从图表中删除。
上述表达式确定各值的 7 天移动平均值,稍后我将介绍该语法。此时,您将看到一组数值,与“原始状态更改计数”数值相比,它们的变化波动要小一些。同样,现在图表也更平滑了。图 21 显示了在我们的服务器上运行此表达式的结果。
图 21
用图表示 7 天的移动平均值;发现线条更平滑了
更改图表类型可使线变得更平滑。现在,它是简单的折线图;如果将其更改为平滑线图,图表中将不显示任何弯折的线条。为此,请执行以下操作:
- 在“布局”选项卡上,右键单击该图表,依次指向“图表类型”、“折线图”,然后单击“平滑线图”。
MDX 代码段
让我们看一下计算移动平均值的表达式:
Avg( [Date].[Date].CurrentMember.Lag(6):[Date].[Date].CurrentMember, [Measures].[State Change Count] )
此表达式使用 MDX 的一小段。在此我想强调的是,您只需掌握极少的 MDX 知识即可实现很多目的。让我们了解一下此表达式的各个部分。
首先是 Avg ,它是使用两个参数的函数(还有许多此类函数)。第一个参数实际上是一个维度值“集”,一个集可包含零项或多项。第二个参数是我们要在表达式中使用的事实。
此表达式使用 Date.Date 维度中的值(参见图 7),您可能想知道为什么该维度中每个名称两旁都有方括号。在这个特定的情况下,方括号是可选的。但是,允许名称之间存在空格,如“State Change Count”;在这些情况下,处理表达式的分析器通过方括号识别名称的开始位置和结束位置。
使用多维数据集构建的所有查询在后台生成 MDX,因此这里的 MDX 表达式结合了查询的其他 MDX。当 Analysis Services 处理 MDX 查询时,它将对查询返回的每一行执行计算。上述表达式中的 CurrentMember 指正在计算的行的当前维度“实例”。CurrentMember 与 Date.Date 维度相关,因此CurrentMember 就是正在处理的行的日期,如 1/1/2007。
紧跟 CurrentMember 的是另一函数 Lag,此函数在树中同一级别的值之间移动。Lag 将移动到比当前值小六的值。例如,如果 CurrentMember 是 1/10/2007,Lag(6) 将返回一个比它早六天的值,即 1/4/2007。最后,通过冒号指示使用范围。也就是说,它表示以下表达式:
[Date].[Date].CurrentMember.Lag(6):[Date].[Date].CurrentMember
上述表达式返回一周之内的一组值,并以正在处理的当前行的日期结束。
换句话说,上述表达式中的 Avg 表达式计算七天内的平均值,并以当前行的日期结束。但是,它对什么内容求平均值?毕竟,它可以对不同的事实求平均值。答案就在第二个函数中,其中指定表达式在计算平均值时要使用哪些事实或“度量值”。表达式使用 [Measures].[State Change Count],意味着它将返回一周时间内“状态更改计数”的平均值,并以计算当前行的日期结束。
查找“度量值”和“维度”名称
您可能还发现了名称 [Measures].[State Change Count] 与图 13 中树的“度量值”分支所示的层次结构不完全一致。那么如何才能获得正确的名称呢?图 22 显示了如何使用“元数据”树获得度量值的完整名称。
图 22
将鼠标悬停在“元数据”区域上方可显示 MDX 名称以及简短的描述
如果从头创建此表达式,而不使用提供的表达式,则使用“计算成员生成器”对话框底部的两个树进行创建通常会比较容易。可通过将鼠标悬停在项目上方查找名称,如图 22 中显示的“状态更改计数”。还可以通过双击树的任一节点将文本添加到表达式文本框中。通过左侧树可访问所有度量值和维度;通过右侧树可访问各种函数和属性。
添加参数
总体来说,该报表非常不错,但通用性还不够。换句话说,您必须在“查询设计器”中修改报表才能更改日期范围。显然,您不希望每位报表用户仅仅为了更改日期范围就将其加载到 Visual Studio 的报告设计器中,而希望他们仅选择日期范围即可。
可以使用参数来执行此类操作。将以下两个参数添加到报表中:开始日期和结束日期。这两个参数用于控制报表中显示的日期范围。此外,您可能不需要硬编码到报表中的项目,以便同一报表可用于任何项目。
指定开始日期和结束日期参数
此时,报表查询包含三个您可能希望转换为参数的筛选,并且此过程分为多个步骤。第一个步骤是标记要将哪些表达式值作为参数。在“查询设计”窗口中,选中包含“日期”维度的行中的两个复选框,如图 23 所示。
图 23
选中日期维度的参数列中的两个复选框以将它们作为参数
因为筛选器是一个日期范围,所以该行中有两个复选框。因此,筛选器的这两个极值均有一个复选框。
选中这两个复选框并不会立即产生效果,所以需要单击“布局”选项卡,然后再次单击“数据”选项卡。此后,您将在“数据集”组合框中看到两个新的数据集:FromDateDate 和ToDateDate。这里解释一下这些奇怪的名称:From 和To 来自 Range 运算符,而DateDate 来自层次结构列。您可能记得,Date.Date 表示我们正在使用日期维度,并且我们正在查看该维度的日期范围,而不是其他类似于 [Year Month Day] 的范围。
如果选择其中一个数据集,您将在我们一直使用的筛选区域中看到一组非常难以理解的代码。此代码就是原始 MDX。幸运的是,您不需要这两个原始的 MDX 数据集。除了创建这两个数据集之外,选中参数列中的两个复选框还会创建两个报表参数,您将使用这两个参数进行实际工作。
执行下一步操作之前,您可以单击“预览”选项卡以运行报表。您将看到可以通过两个参数设置开始和结束日期,如图 24 所示。但是,这些参数也存在一些问题。首先,它们显示为组合框而不是日历控件,并且具有非常长的日期列表。其次,标签显示内部名称,而不是那种用户友好的标签。
图 24
两个日期参数显示为非常长的列表框
日期参数显示为非常长的列表而不是日历控件,原因是在报表参数对话框中将其定义为字符串而不是 DateTime 类型。要查看此设置,请单击“报表”菜单 上的“报表参数”。
遗憾的是,无法直接将参数类型更改为 DateTime 并使其生效。为什么呢?嗯,这个问题有点令人费解。在假定来自参数的任何值都是字符串而不是任何其他类型的情况下,“查询生成器”界面将在后台构建 MDX 查询。此外,字符串通常必须是特定的格式,稍后您会看到这一点。解决方法是创建新的可见参数集,其数据类型为 DateTime。然后,使用某些表达式正确地对字符串进行格式化,从而 FromDateDate 和 ToDateDate 参数便可以隐藏起来并从两个可见的参数中获得它们的值。
首先添加两个新的参数并隐藏两个现有的参数。为此,请执行以下操作:
- 在“报表”菜单上,单击“报表参数”(如果“预览”选项卡处于活动状态,此命令将不可用)。
- 在“参数”列表下,单击“添加”。
- 在“名称”框中,输入 FromParameter。
- 在“数据类型”框中,输入 DateTime。
- 在“命令提示符”框中,输入 Start Date。
- 在“默认值”区域中,单击“无查询”选项按钮,然后在按钮右侧的文本框中输入以下表达式:
=DateAdd("m",-1,Today())
- 单击“参数”列表框右侧的向上箭头,以使此新参数在列表中位于 FromDateDate 和 ToDateDate 参数的上方。
- 重复上述步骤,再创建一个名称 设置为 ToParameter、命令提示符 为End Date的参数,并使用以下表达式:
=Today()
单击“预览”选项卡可查看此设置的效果。您将看到两个参数集。新参数集显示非常漂亮的弹出日历,您可以在其中选择开始和结束日期。但是,此时它们尚未连接到查询。顺便说一下,下面很快介绍为何需要将这两个参数添加到由查询生成器创建的两个参数的上方(旨在控制计算顺序,即从上到下)。
下一步是修改这两个自动生成的参数,以使它们将两个新参数中的值转换为格式正确的字符串。为此,请执行以下操作:
- 打开“报表服务”对话框(从“报表”菜单中)。
- 单击 FromDateDate 参数。
- 在“可用值”部分中,单击“无查询”选项按钮。
- 在“默认值”部分,在表达式文本框中输入以下文本:
="[Date].[Date].&[" + CDate(Parameters!FromParameter.Value).ToString("s") + "]"
- 对 ToDateDate重复上述步骤,但不使用以下表达式:
="[Date].[Date].&[" + CDate(Parameters!ToParameter.Value).ToString("s") + "]"
- 单击“预览”选项卡。
现在,FromDateDate 和 ToDateDate 参数的值如下所示:
[Date].[Date].&[2007-03-16T00:00:00]
为什么是这个奇怪的值?此值是使用 MDX 进行格式化的。第一个 Date 是维度的名称。第二个 Date 表示使用日期范围而不是使用其他任一可用的范围选项(如 [Year Month Day])。& 号字符表示这是实际值,并且结尾处的值是格式化为非特定于区域设置格式的日期。吆!
借助“查询生成器”窗口中的元数据浏览器,您可以了解对于此类字符串应使用哪种格式。向下导航所需维度树,然后深入查看树的成员/所有分支。将鼠标置于其中一个值的上方会出现工具提示,显示一个示例以提示您应该对该维度采用哪种字符串。
既然这一切工作正常,您可以隐藏两个自动生成的参数并删除那两个难以理解的 MDX 查询。为此,请执行以下操作:
- 打开“报表参数”对话框。
- 单击 FromDateDate 参数,然后选择“内部”复选框。
- 单击 ToDateDate 参数,然后选择“内部”复选框。
- 关闭对话框。
- 在数据集 组合框中,单击 FromDateDate 数据集。
- 单击“删除所选的数据集”工具栏按钮(该按钮上有一个红色 X)。
- 重复这些步骤以删除 ToDateDate 数据集。
总的来说,在“查询设计器”窗口中选中筛选区域参数列中的复选框就表明您开始创建自定义参数。这将创建一个或多个数据集和一个或多个报表参数。如果要删除自动生成的 MDX,需要创建新的参数集并隐藏原有参数集。此外,您需要修改原有参数集以将新参数集中的值以字符串的形式格式化为正确的 MDX 格式。
使用“默认”项目
您创建的查询仅适用于某个特定报表,并不适用于一般报表。理想情况下,报表将显示当前项目的结果。当将报表部署到 TFS 时,实际上是将其部署到了服务器上的单个项目中,因此,当前项目是指包含已部署的报表的项目。如何获取此信息?您可以使用表达式和名为ReportFolder 的“全局”集合中的值进行获取。
此处显示的解决方案虽然并不理想,但是它确实有效。有一个问题是:我们需要使用一个项目参数,此参数在您进行开发时可见,而在部署报表前隐藏。您需要这种效果,是因为在 Visual Studio 中运行报表时ReportFolder 值为空 — 它只有当报表在 SQL Server Reporting Services 上运行时才会具有值。要使报表自动使用包含您的报表的项目,请执行以下操作:
- 在 dsBugRates 数据集中,选中“团队项目”维度的“参数”列中的复选框。
- 切换到“布局”选项卡以生成参数和数据集。
- 打开“报表参数”对话框。
- 单击“添加”按钮,然后创建名为ExplicitProject 的参数(可以将所有其他设置保留原样)。
- 将此参数移动到“参数”列表的顶部。
- 将 TeamProjectTeamProject 参数移动到“参数”列表的第二个位置。
- 选择 TeamProjectTeamProject 参数,并进行下列更改:
- 选中“内部”复选框。
- 清除“多值”复选框。
- 选中“允许 null 值”和“允许空值”复选框。
- 在“可用值”部分和“默认值”部分中均选中“无查询”选项按钮。
- 将以下表达式置于“默认值”表达式文本框中:
="[Team Project].[Team Project].[" + IIF(LEN(Globals!ReportFolder) > 0, SPLIT(Globals!ReportFolder,"/").GetValue( IIF(split(Globals!ReportFolder,"/").Length > 1, 1, 0)), Parameters!ExplicitProject.Value ) + "]"
- 在“数据”选项卡上,单击“数据集”组合框中的 TeamProjectTeamProject 数据集,然后单击“删除所选的数据集”工具栏按钮将其删除。
此时,如果切换到“预览”选项卡,您将看到一个错误,不过在这种情况下出现该错误属于正常现象。图 25 显示了您将看到的内容。
图 25
切换到“预览”选项卡时出现的错误消息
在“显式项目”参数文本框中,键入您的项目名称,然后单击“查看报表”来查看该项目的报表。
做好项目部署准备后,您希望将 ExplicitProject 参数设置为“内部”后,然后将其放置到服务器上。
发布报表
对于部署项目,现在可能是查看如何将报表部署到 TFS Reporting Services 服务器的最佳时机。这实际上非常简单,但并不一定非常明显。Visual Studio 在启动后可以直接将报表部署到您选择的项目中。为此,请执行以下操作:
- 在“解决方案资源管理器”中,右键单击报表项目,然后单击“属性”。此时将显示“属性”窗口。
- 确保将 OverwriteDataSources 设置为 False。这样将保留在报表服务器上为您的项目创建的数据源。
- 对于 TargetDataSourceFolder,输入您的 TFS 项目名称,因为它是 TFS 在报表服务器上为项目报表创建的文件夹的名称。
- 对于 TargetReportFolder,输入您的 TFS 项目名称,后跟任意子文件夹(如果使用文件夹组织报表)。
- 对于 TargetServerURL,该内容应该类似于 http://<服务器名称>//reportserver,如图 26 所示。
图 26
允许将报表部署到 TFS 项目的设置示例
设置部署选项后,可以右键单击“解决方案资源管理器”中的报表,然后单击“部署”将报表添加到服务器(或者对其进行更新)。
数据集中的参数
选中“查询生成器”的“参数”列中的复选框时,通常会创建一个新的数据集,以便您填充参数的值。生成的代码是难以维护的自定义 MDX。我喜欢用自己的使用“查询生成器”的查询替换这些生成的查询,以便更容易地进行读取和维护。
例如,您可能希望在错误率报表中添加“状态”参数,以便限制用图表示的状态。
筛选器
有时,查询返回的数据要多于您实际需要的数据。如果添加了新列,结果收到原有列为空值的行,往往就发生这种情况。通常,查询结果不包含所有列中的值为空的行,所以添加具有值的列可以显示之前由于所有值为空而未返回的其他行。
如何删除这些多余的行?通过筛选器进行删除,这适用于数据集结果。换句话说,在将行传递到报表之前,筛选器会将这些行从数据集中删除。
使用数据集属性对话框将筛选器添加到数据集中。要打开此对话框,单击数据集组合框右侧的省略号按钮 (…),然后单击“筛选器”选项卡。
您需要添加一个类似于以下内容的筛选器(其中表达式取决于要筛选的实际列):
表达式 | 运算符 | 值 |
---|---|---|
=IsNothing(Fields!Current_Work_Item_Count.Value) | = | =False |
需要特别注意的是值前面要有等号。没有等号,该值将被视为字符串。但是,IsNothing 函数会返回一个布尔运算符,所以您会收到一个类型不匹配错误。
更多参考资料
本文只对 SQL Reporting Services 和 Analysis Services 进行了简要介绍。我们计划在我们的网站上继续提供更多的信息。此外,MSDN 上的下列资源可能会有所帮助: