适用于:
Microsoft SQL Server 2005 Reporting Services
摘要:本白皮书介绍了在 Microsoft Server Reporting Services 报表内设计图表的常规信息、最佳实践及技巧。本文概述了一些 Reporting Services 功能,回答了有关图表设计和功能的常见问题并包括了如何设计更优秀图表的高级示例。
单击此处可下载相关示例代码,GetMoreChartsSamples.exe。
单击此处可下载本文的 Word 版本,MoreSSRSCharts.doc。
本页内容
简介
数据准备
图表标签
示例图表和报表
结束语
简介
本白皮书讲述如何在 Microsoft SQL Server Reporting Services 报表中设计图表。本文分为几部分并引用特定的报表示例;它们包含在示例项目下载中。
第一部分为数据准备,此部分主要包括有关准备数据的特定信息、技巧和见解。第二部分为图表标签,此部分讲述如何应用标签设置来改进图表和控制视觉外观和效果。
示例图表和报表部分讲述如何充分利用 SQL Server Reporting Services 内置图表功能的特定示例,有时还包括高级示例。其中有些示例需要根据所提供的分步说明进行仔细研究。为方便起见,还包括了功能完备的示例报表。这些示例报表基于 SQL Server 2005 AdventureWorks 示例数据库和 Northwind 示例数据库。
关于数据准备和图表标签的信息可帮助您更好地理解这些示例。当您研究这些示例时,您会发现偶尔跳回到第一部分所介绍的特定图表标签主题很有用。
数据准备
图表提供了一种实现数据可视化的方法。与冗长的数据列表相比,图表可以更有效地传递信息。在创建图表前花费时间认真地准备和理解数据,将有助于快速高效地设计图表。Reporting Services 图表数据分为三个区域:值、类别组和系列组。有关详细信息,请参阅 SQL Server 2005 Books Online 的 SQL Server Reporting Services 部分中的使用图表数据区域。
图表与矩阵非常相似:
-
图表的类别组相当于矩阵的列组。
-
图表的系列组相当于矩阵的行组。
-
图表的值相当于静态矩阵的行组。
-
图表的数据值或数据点相当于矩阵的单元。
准备图表的数据集查询时请牢记以下几点:
-
图表值沿数字 Y 轴显示。请确保作为值所使用的字段为数字数据类型(与包含格式化数字的字符串不同)。
-
X 轴的值根据图表类别组的值或组标签(如果组标签已显式定义)来确定。X 轴支持两种模式(将在 X 轴类别模式和标量模式中详细论述)。如果希望使用 X 轴标量模式,请确保用于类别组表达式的字段和/或表达式的计算结果为数字数据类型或 DateTime 对象。
-
您可以随意使用任意数量的图表。图表同其他数据区域(如矩阵或表格)一样,绑定于一个特定的数据集。可以在数据集查询中使用“连接”和“联合”以包含数据集中的所有必需数据。
-
如果图表置于表组头或组尾中,或置于矩阵单元中,则传递到图表控件中的数据会限制为构成该组的数据的子集。图表不能置于表的细节行中,因为只引用一个数据行。
-
若图表中数据过多(如几千个数据点),则解释起来会很困难,除非使用散点图来显示数据点的值和群集的分步。如果详细级别的数据粒度并非必需或没有用处,则可以考虑在数据集查询中预先聚合数据。
图表标签
本节包含下列图表标签主题。当您研究下节中的示例时,您会发现偶尔跳回到本节所介绍的主题很有用。
-
X 轴类别模式和标量模式
此节说明两种 X 轴模式之间的显著区别。可以使用 CategoryAxisSettings 示例报表作为试验的起点。
-
轴标签
轴标签部分对应用标签设置及在运行时这些设置如何影响图表的视觉外观进行深入讨论。
-
数据点标签和图例标签
此节讲述如何通过添加数据点标签和图例标签来改进图表。
X 轴类别模式和标量模式
X 轴有两种模式。可使用“图表属性”对话框中“X 轴”选项卡上的“数值或时间刻度值”选项来设置模式。
-
类别模式
类别组表达式的值确定 X 轴的各个类别。标签仅针对在数据中的实际类别显示。在类别模式中,组内的排序顺序和显式排序表达式很重要,因为图表控件将不再对类别重新排序。针对 X 轴所定义的格式代码仅在组表达式或组标签表达式(如果已显示定义)计算结果为非字符串对象时才会应用。
如果存在多级类别分组,则会显示类别的分组跨度。
-
标量模式
X 轴的值范围由最小和最大类别组表达式的值决定。因此,为了可以比较和排序,组表达式的值必须为数值或 DateTime 值。数据中的间隙(例如,使用 DateTime 类别分组却只有七月和九月的数据)在 X 轴上显示,类别则按比例绘制到数字或 DateTime 轴。在标量模式中只允许一个类别分组。
图 1A 和 2A 中的图表显示相同四周的订单数据。
图 1A 中的类别轴模式
由于在底层数据集中没有周末(周六和周日)的订单数据,因此在图 1A 中未显示该类别。示例使用两种类别分组,如图 1B 所示。内部组表达式使用 =Day(Fields!OrderDate.Value) 来按天分组。外部组表达式使用 =Month(Fields!OrderDate.Value) 来按月分组。
注意 外部组标签表达式定义为 =MonthName(Month(Fields!OrderDate.Value)),该表达式使用月名作为分组跨度的标签。
图 1C 显示了 X 轴属性的设置。在类别模式中,最小、最大和间隔的语义均基于类别索引。因为没有指定任何显式轴的属性,所以所有类别的数据均显示一个标签。
图 2A 中的标量轴模式
标量模式中的 X 轴显示数值或 DateTime 值。X 轴涵盖了最小值和最大值范围之间的全部值。因此,图 2A 包含周末对应的间隙,因为它们没有订单数据。
在标量模式下使用 X 轴时仅允许一个类别分组。类别分组的值必须计算得出数值或 DateTime 值。X 轴标签的格式由 X 轴上的格式字符串设置确定,本例中为 MMM dd。图 2B 显示了 X 轴属性的设置。
有关数字和 DateTime 格式字符串的详细信息,请参阅 Microsoft Developer Network (MSDN) 上 .NET Framework 开发人员指南中的以下页面:
-
标准数字格式字符串
-
自定义数字格式字符串
-
标准 DateTime 格式字符串
-
自定义 DateTime 格式字符串
轴标签
Y 轴标签始终基于数值。如果未指定显式轴设置,则 Y 轴使用自动缩放模式,如下所述:
-
Y 轴的最小值根据所有数据点中最低的 Y 值而定。如果最小的数据值不是整数值而是双精度值(比如 3.75)且关闭了侧边距,则您会发现 Y 轴标签未四舍五入为整数(例如,间隔为 1:3.75、4.75、5.75 等等)。
-
Y 轴的最大值根据所有数据点中最高的 Y 值自动确定,除非显式指定最大值。
-
Y 轴的主要间隔根据数据值自动确定(在图 3 中自动主要间隔为 20)。
-
Y 轴的次要间隔将主要间隔分成若干段(在图 3 中自动次要间隔为 4;因此 20 / 4 = 5 个次要间隔段构成一个主要间隔段)。
-
由于 Y 轴的值始终为数字,所以可以直接应用数字格式字符串。此设置会应用于所有已生成的 Y 轴标签。
X 轴模式
如先前部分所述,X 轴有多种模式。根据不同的模式,会提供不同的格式选项,轴设置(最小值、最大值、交点等等)也可能有不同的解释。下面对不同的格式选项进行说明:
-
基于数字类别组值的标量模式
采用这些设置,X 轴非常类似于 Y 轴。轴设置(如最小值、最大值、交点、主要间隔及次要间隔)被解释为整数值或双精度值。
由于 X 轴的值为数字,所以可以直接应用数字格式字符串。
-
基于 DateTime 类别组值的标量模式
轴最小值:如果轴最小值设为常量(比如 2005)或具有整数结果的表达式(比如 =2005),则此值会被解释为该年的第一天(如 2005 年 1 月 1 日)。
轴最大值:整数设置被解释为该年的最后一天(如 2005 年 12 月 31 日)。
轴交点:设置被解释为年中。
主要间隔和次要间隔:间隔设置被解释为天(相当于 OADate 格式)。例如,5 表示 5 天的间隔,0.5 表示半天(12 小时)的间隔。
对于标签格式设定,可以直接应用标准 DateTime 格式字符串。
-
类别模式(未选择数值或时间刻度值选项)
图表控件基于类别组表达式值,将类别在多个系列中匹配(例如,2006 系列中类别为一月的数据将与 2007 系列中类别为一月的数据处于同一集合。
只有类别组表达式(或如图 4 中的标签表达式)的计算结果为数字或 DateTime 数据类型时,X 轴选项卡上的格式字符串设置才有效。通常使用类别模式时,类别组表达式的计算结果为字符串对象,因此随后应用的格式代码无效。可添加或更改类别组标签表达式,或直接通过标签表达式应用格式设置,如图 4 所示。
注意 在类别模式中,最小值、最大值和间隔的语义均基于“类别索引”。例如,将 x 轴最小值设置为 2 意味着不会显示第一个类别的数据。将主要间隔设置为 5 意味着在 x 轴上仅每隔 4 个类别显示标签。这在 x 轴有很多类别(和标签)且类别的底层语义实际为数字时很有用。
注意 Reporting Services 2005 也允许在“X 轴”和“Y 轴”选项卡中显示的所有输入字段(“标题”、“最小值”、“最大值”、“主要间隔”、“次要间隔”等)中使用表达式。
轴标签格式设置问答
-
问( Y 轴):如何可在 y 轴上实施“准确的”基于整数的标签?
答:如果未指定轴设置,则图表控件将基于数据点 y 值自动确定这些值。如果数据点的最小值/最大值不是整数,y 轴标签则可能使用双精度值。
不过,如果至少有一个轴设置(如“最小值”或“交点”)被报表作者显式指定为整数值,则图表控件会将自动检测到的值圆整为最接近的整数值,然后显示“准确的”标签。例如,可动态设置 y 轴的最小值,并如此进行圆整:=Floor(Min(Fields!Freight.Value))。
-
问(标量 x 轴):启用“数值或时间刻度值”导致在运行时图表未显示任何数据点。哪里出现了问题?
答:很可能是因为类别组表达式的计算结果为字符串而不是数值。相应更改类别组表达式。如果不想更改获取标量数据值而不是字符串值的查询,还可在报表中使用 Microsoft Visual Basic 函数(如 CInt()、CDbl() 或 CDate())执行类型转换。
-
问(类别 x 轴):如果类别数量增加,x 轴将变得拥挤,最终将不再绘制轴标签。如何能够控制类别模式下 x 轴的标签数量?
答:图表控件会尝试自动定位 x 轴标签以避免重叠标签文本。默认情况下,每个类别在 x 轴上均具有一个标签。可显式设置 x 轴主要间隔设置以覆盖此默认行为。例如,将主要间隔设置为 5 将仅每隔 4 个类别显示标签。
-
问( X 轴):X 轴标签是如何自动定位的?
答:当前,内置的 Reporting Services 图表仅允许自动定位以避免覆盖 x 轴标签。轴标签的标签方向(水平/垂直)取决于标签字符串大小和可用空间。X 轴标签可以一行水平显示、以能够换行的多行水平显示或垂直显示。当前并不支持以某一角度显示 x 轴标签或对单个 x 轴标签位置显式手动控制。
注意 有多个第三方图表加载项,能够对轴标签进行更多控制。这些加载项可安装在 Reporting Services 2005 上。
数据点标签和图例标签
数据点标签可专用于指出图表中所有可见数据点中的某些值(如总体最小值或最大值)。
要启用数据点标签,请在“图表属性”对话框中编辑图表值。这将打开“编辑图表值”对话框,其中含有“点标签”选项卡,该选项卡具有“显示点标签”选项。
定位数据标签
打开数据标签时,默认情况下,每个数据点将显示一个标签。数据点标签将自动定位以避免重叠标签。如果数据点标签重叠,图表控件会将重叠标签移至图表绘图区的空闲空间中(并画出轮廓以将数据点标签与数据点值相连)。如果太多标签重叠,图表控件将删除个别数据点标签,直至有足够的空间可放下其余标签而不会重叠。
除了自动定位外,还可使用显式“手动标签定位”(顶部、靠左、居中等)。不过,视数据值以及数据点标签的长度和大小而定,这可能导致重叠标签。
默认情况下,数据点标签将显示数据点的 y 值。也可指定显式数据点标签表达式和数字或 DateTime 格式字符串来自定义标签。通常,会使用类似于用于计算数据点值表达式中 y 值的表达式执行数据点标签计算。例如,要在该部分所占的相对份额大于总量的 5% 时只显式数据点标签,可使用类似以下步骤中代码的数据点标签表达式。
-
对于数据点标签表达式,请使用以下表达式:
=Code.GetLabel(Sum(Fields!Sales.Value), Sum(Fields!Sales.Value,"SalesChart"))
-
打开“报表属性”对话框,然后单击“代码”选项卡。在“自定义代码”选项中添加以下 GetLabel(…) 自定义代码函数。
Public Function GetLabel(ByVal currentValue As Double, ByVal totalValue As Double) As String If currentValue / totalValue < 0.05 Then Return " " Else Return Format(currentValue / totalValue, "P1") End If End Function
代码解释
GetLabel() 函数有两个参数。第一个参数提供该特定数据点的当前值。第二个参数提供总量计算。此函数计算相对百分比。如果它小于 5% (0.05),将返回一个具有空格的字符串。
注意 返回一个空值或空字符串将显示自动生成的默认标签。如果相对百分比至少为 5%,则将返回百分比格式字符串(格式字符串:P1)。
可在此白皮书随附的 PiePercentage 示例报表中找到应用此种格式的示例。
饼图和圆环图数据标签位置
对于饼图和圆环图,只有两种数据点标签位置:内部(将数据点标签位置设置为“自动”或“居中”)和外部(任何其他标签位置)。图 5(以及 PieSimplePercentage 示例报表)中显示了外部标签示例。
可如图 6 所示指定饼部分标签的位置。
图例标签
通常,基于动态系列组值或标签(如在组上显式指定)和(静态系列)值的名称确定图例标签。因为图表实质上是分组层次结构的扁平化表示,所以图例标签基于该层次结构生成。
例如,如果图表含有两个系列分组(外部的定义为 OrderYear,内部的定义为 OrderQuarter),且仅有一个图表值(如 Actual),图例标签将通过将组值和图表值串联生成,如表 1 所示。
表 1OrderYear 标签
OrderQuarter 标签
图表值系列标签
生成的图例标签
2006
Q1
Actual
2006 – Q1 – Actual
2006
Q2
Actual
2006 – Q2 – Actual
假设我们再添加一个名为 Budget 的图表值。使用与上一示例相同的数据,所生成的标签将如表 2 所示。
表 2OrderYear 标签
OrderQuarter 标签
图表值系列标签
生成的图例标签
2006
Q1
Actual
2006 – Q1 – Actual
2006
Q1
Budget
2006 – Q1 – Budget
2006
Q2
Actual
2006 – Q2 – Actual
2006
Q2
Budget
2006 – Q2 – Budget
注意 通过将组标签表达式设置为返回空字符串 (=""),可隐藏层次结构中的单个内部级别。这将从生成的图例标签中删除该组级别。
空数据点和标签
以下情况您可能会觉得很熟悉。您构建了一个具有一个数据系列的图表,数据点标签已打开,此图表看上去很好。您决定添加一个动态系列组以便该图表显示多个数据系列。可此时该图表却具有额外标签(用于空数据点)。
在底层数据集对每个系列/类别组合都不包含数据值时,会出现空数据点。此图表实质上等同于一个具有空单元的(稀疏)矩阵。
可删除空数据点的标签。使用此白皮书随附的 EmptyDataPointLabels 示例报表中所示的方法(另请参阅图 7),而不打开数据点标签和使用默认标签。以下是执行此操作的示例代码。
-
使用 Count(…) 函数确定为此数据点聚合多少底层数据集行。如果计数为零,则这是个空数据点。通过实际的标签值将计算值传入自定义代码函数:
=Code.GetLabel(Avg(Fields!UnitsInStock.Value), Count(Fields!UnitsInStock.Value))
-
打开“报表属性”对话框,然后单击“代码”选项卡。在“自定义代码”选项中添加以下 GetLabel(…) 自定义代码函数。
Public Function GetLabel(ByVal datapointValue As Double, ByVal count As Integer) As String If count = 0 Then Return " " Else Return Format(datapointValue, "N1") End If End Function
数据点标签格式设置问答
-
问:在图表充满数据点和标签时,靠近数据点标签的灰线(称为轮廓)有何用途?
答:如果数据点标签位置设置为“自动”,则图表控件会将标签移至空闲空间区中以避免重叠数据点标签。这些轮廓将通过数据点位置连接数据点标签。
可使用手动定位避免此情况。使用表达式,可通过提供具有空格 (=" ") 的字符串的计算结果动态隐藏大部分数据点标签。否则,将在打开数据点标签时显示默认标签。
-
问:标签格式设置可使用 Dundas 关键字吗?
答:可以,可对数据点标签使用内置的 Dundas 关键字。不过,通常建议您不要同时将 RDL 表达式和 Dundas 关键字同时使用(RDL 表达式将首先被计算,Dundas 函数将由图表控件随后进行解释)。表 3 含有有用的 Dundas 关键字的列表。
Dundas 关键字
替换为
#VALX
数据点的 X 值
#VAL
数据点的 Y 值
#VALY、#VALY2、#VALY3 等
第一个 y 值、第二个 y 值、第三个 y 值等
#INDEX
系列内的数据点索引
#TOTAL
当前系列中所有 y 值的总计
#VALY{C2}
使用 C2 格式字符串(货币格式)设置格式的数据点的 Y 值
示例图表和报表
本节包含了创建不同类型图表和报表的示例。您可能会发现在研究这些示例时,有时回到先前小节中介绍的图表标签主题会很有帮助。以下是本节介绍的一些示例。
-
柱形图和折线图混合图表
大体介绍了组合图表和 SalesCostTarget 示例报表。
-
Pareto 图表
实现图表的 Pareto 可视化(ParetoChart 示例报表)。
-
移动平均计算
图表中时间系列趋势的计算和可视化(MovingAverage 示例报表)。
-
自定义图表调色板和图例
如何自定义图表中的颜色(CustomColorPalette 示例报表)。
-
饼图和圆环图
使用饼图和圆环图时要注意的具体信息。
-
添加图表数据表
说明如何将聚合的图表数据链接到明细数据(PiePercentage 示例报表)。
-
散点图和气泡图
设计散点图和气泡图的重要提示(BubbleChart、StepFunctionChart)。
-
表格内嵌图表
也许您不需要复杂的图表显示,或者您必须在运行时处理未知数量的数据,但仍希望有用、良好的显示。本节介绍了实现此目标的途径 (TableInlineCharts)。
-
图表可扩展性和手动创建图表
讨论在内置图表不够时有何选择。
在下载文件中,与此白皮书一起还包含了基于 SQL Server 2005 AdventureWorks 示例数据库和 Northwind 示例数据库的一些示例报表。
柱形图和折线图混合图表
将一些数据系列显示成柱形并将其他数据系列显示为线条的图表常用于说明总体趋势、目标值或用于深入分析图表中的数据。本节介绍了关于如何在 Reporting Services 中设计这种图表的通用信息。
创建柱形图和折线图混合图表:
-
将一个图表添加到报表,并将图表类型设置为“柱形图”。
-
通过添加类别组和/或系列组以及数据值来设计图表。
-
对于要用折线显示的数据值,在“报表设计器”中执行以下步骤:
-
打开“图表属性”对话框。
-
单击“数据”选项卡。
-
选择要显示为折线的数据值,然后单击“编辑”。
-
在“编辑图表值”对话框中,单击“外观”选项卡,再选择“将数据绘制为线条”(见图 8)。
图 8. 在柱形图中将数据系列绘制成折线
-
将常数或动态目标值添加到图表:
-
设计图表。
-
在“图表属性”对话框中的“数据”选项卡上,添加新的数据值(例如,Target)。
-
设置目标值(图 9 中的示例使用了横跨所有类别的常数目标值 100000)。请确保使用以 = (等号)开始的表达式。否则,不会将目标值解释为数字值。
图 9. 添加目标值
SalesCostTarget 示例报表(见图 10)使用此方法在图表中添加了一条简单的销售目标线。
注意 由于折线图系列的线条是通过连接多个类别的数据点来绘制的,因此只有在类别分组至少有两个在运行时不同的组实例值时才能看到线条。
注意 如果图表包含一个或更多的系列组,目标数据值对于每个系列组实例都相同。如果每个组实例都具有特定的目标值,这可能会很有用。
如果您只希望将一个全局目标值用于所有系列,可以按以下方式动态设置目标数据值:
=iif(Fields!.Value = First(Fields! .Value, ), , Nothing)
具体的表达式实例可能与以下代码类似:
=iif(Fields!Year.Value = First(Fields!Year.Value, "SalesChart"), 100000, Nothing)
Pareto 图表
Pareto 图表概括并显示了数据组之间差异的相对重要性。Pareto 图表可将“致命的少数”和“有用的多数”区别开来。还可将 Pareto 图表定义成柱形以降序排列的柱形图,以识别最大的改进机会。
尽管内置的 Reporting Services 图表目前并不直接支持 Pareto 图表,但是您可以使用 Reporting Services 2005 功能并编写一些代码来创建 Pareto 图表。本节深入解释了此白皮书附带的 ParetoChart 示例报表。
以下是 ParetoChart 示例报表的方案描述。
SQL Server 2005 AdventureWorks 数据库包含关于销售雇员的数据。我们对分析以下与我们的销售雇员有关的信息特别感兴趣:
-
如果以获得去年最高奖金的销售雇员为基础,Pareto 分析会是什么样子?(见图 11 中的橙色线。)
-
去年哪些雇员领取到最高奖金?这与他们今年的总销售额相比结果如何?(见图 11 中的蓝色和绿色圆柱。)
-
将去年的奖金与今年的销售额相比较,在销售业绩上有没有重大变化?虽然可通过比较蓝色和绿色的圆柱对此做出解答,但图 11 中橙色和红色 Pareto 线条的巨大差距使结果更加明显。 对于某个具体的销售雇员,我们想深化其过去和现在的销售业绩以根据多年的数据分析历史趋势。
这一点可通过增加对销售数据值(图 11 中的绿色圆柱)的钻取操作来实现,因为通过钻取操作可详细分析个别销售人员的数据而完成趋势分析。钻取报表(MovingAverage 示例报表)将在下一节详细说明。
构建 Pareto 图表报表
-
定义检索必需销售数据的查询。
该查询将根据奖金值对每个销售人员的数据进行预排序。它不会检索独立的销售订单,因为此报表不需要它们。这样也可使数据集尽可能小。
-
设计总体图表布局。
将柱形图添加到报表中。若要分析每个销售人员的数据,需基于销售人员添加一个类别组(分组依据 =Fields!SalesPersonID.Value)。对于类别标签,显示销售人员的名和姓。这一点可通过将类别组标签表达式设为以下形式来实现。
=Fields!FirstName.Value & " " & Fields!LastName.Value
-
准备图表以进行 Pareto 计算。
我们将 RunningValue(…) 函数用于计算。
注意 仅在从 Reporting Services 2005 开始的图表中才支持 RunningValue 函数。
与矩阵相似,RunningValue 函数需要图表中明确的分组范围,以确定运行值是否应跨越某个特定数据系列的所有类别运行(实质上是水平方向),或者是否应跨越某个特定类别的所有数据系列运行(比较少见)。
对于 Pareto 计算,我们需使用 RunningValue 函数以使用一个系列组名称作为其“重置”范围(因此跨越所有类别运行)。由于我们还没有用于此特定图表的系列组,因此我们可只根据常数值(例如 1)添加一个假系列组。
组表达式:1
标签表达式:=""(从生成的图例标签中隐去系列标签)
结果将产生一个数据系列,同时提供一个明确的系列范围名称。
-
将奖金和销售数据值添加为圆柱。
通过将相应的数据集字段拖到图表上,可将 Bonus 和 SalesYTD 的数据值添加到图表中。
注意 本示例对奖金和销售值使用了 Sum() 聚合函数。
我们想将图例放在图表绘图区的左上角。因此,我们希望缩放 y 轴,以便图表中显示的最大数据点值不超过 y 轴总高度的 75%。
我们通过将奖金计算按 75% 的系数缩放实现此目标:
=0.75 * Sum(Fields!Bonus.Value) / Max(Fields!Bonus.Value, "SeriesGroup")
我们对销售计算执行相同的操作:
=0.75 * Sum(Fields!SalesYTD.Value) / Max(Fields!SalesYTD.Value, "SeriesGroup")
-
将 y 轴设置为百分数轴。
在上一步中,我们将奖金计算和销售计算设置为百分比计算(相对最大值的数量)。
将 y 轴的格式字符串设为 P0 将应用百分比格式(实际 y 轴将在 0.0 和 1.0 之间按比例划分)。为获得良好的间隔,我们将 y 轴的主要间隔设置为 0.2 以设置 20% 的间隔。
-
将奖金和销售的 Pareto 计算添加为线条。
RunningValue() 函数在重置之前一直进行累积计算。我们希望永不重置。由于我们起初没有明确的系列组,因此我们在步骤 3 添加了一个系列组。
Pareto 计算是指累加和除以总值。对于奖金 Pareto 计算,我们使用以下表达式。
=RunningValue(Fields!Bonus.Value, Sum, "SeriesGroup") / Sum(Fields!Bonus.Value, "SeriesGroup")
我们对销售 Pareto 计算执行相同的操作:
=RunningValue(Fields!SalesYTD.Value, Sum, "SeriesGroup") / Sum(Fields!SalesYTD.Value, "SeriesGroup")
-
添加针对销售数据值的钻取操作。
要启用对 MovingAverage 示例报表中单个销售人员的销售数据进行钻取分析,我们需要添加针对销售数据点的钻取操作(见图 12)。由于我们只对某一个特定的销售人员感兴趣,因此我们将 SalesPersonID 钻取参数设为当前类别组的值。在本例中为当前销售人员 ID:=Fields!SalesPersonID.Value。
图 12. 添加钻取操作 -
通过添加格式、数据点标签和图例完成图表。
移动平均计算
移动平均是用于分析时间系列数据的一系列类似的统计技术之一。可对任何时间系列计算移动平均系列。
虽然内置的 Reporting Services 图表不直接支持移动平均计算,但是您通常可编写代码来执行此类计算。本节深入讲解了 MovingAverage 示例报表。
该示例报表与上一节有关 Pareto 图表中说明的方案有关。对于某个具体的销售雇员,我们想分析其过去和现在的销售业绩以根据多年的数据分析历史趋势。移动平均可用于消除短期波动,以此强调长期走向或周期。
MovingAverage 示例说明了如何计算简单的移动平均数(先前 n 个数据点的未加权平均数)。在这个具体示例中,我们使用了先前三个月的销售数据。请参见图 13。
构建报表
-
定义查询以便其检索必要的销售明细数据。
该查询需经过参数化,以便只检索一个特定销售人员的数据。根据报表参数(由一个基于有效值列表的数据集填充)设置查询参数。
-
设计总体图表布局。
将柱形图添加到报表中。对 x 轴使用类别模式,以便您能拥有两个分组级别:在内部级别按月分组,在外部级别利用分组跨度按年分组。月份组使用以下的显式组标签表达式将月份格式化为缩写的月份名称。=Format(Fields!OrderDate.Value,"MMM")
-
准备图表以进行移动平均计算。
如在 Pareto 计算的步骤 3 中一样,我们使用 RunningValue 函数。移动平均计算不能跨越类别重置,因此我们根据 SalesPersonID 添加系列分组。由于已基于特定的销售人员对查询进行过参数化,因此将只有一个销售人员系列。该系列组标签表达式被设为 =Fields!FullName.Value,因此图表的图例项将包含销售人员的全名。
-
将销售计算添加为圆柱。
将 TotalDue 数据集字段拖到图表值放置区以根据 Sum() 聚合添加销售数据值。若要将词“Sales”连接到系列组标签(如步骤 3 中所定义,该组标签为销售人员的全名),我们可将数据值标签显式设置为 Sales。
-
添加移动平均自定义代码函数。
下表说明了通过使用队列进行移动平均计算的一个示例。“队列内容”列显示了一个具体月份的当前队列内容。表的最后一列基于聚合添加到队列和从队列删除的项来显示 RunningValue 计算。表下方的代码示例说明了此算法的实现。
表 4月份
销售额
移动平均(2 个月)
定期运行值
队列内容
删除的队列值
基于队列的运行值
一月
20
不可用
20
20
不可用
0
二月
10
15
35
20, 10
不可用
0+ 15 = 15
三月
24
17
59
10, 24
-20
15+ (24-20) /2 = 17
四月
16
20
75
24, 16
-10
17+ (16-10) /2 = 20
五月
12
14
87
16, 12
-24
20+ (12-24) /2 = 14
要实现基于队列的 RunningValue,请将以下代码添加到“报表属性”对话框“代码”选项卡的“自定义代码”部分。
Private queueLength As Integer = 3 Private queueSum As Double = 0 Private queueFull As Boolean = False Private queue As New System.Collections.Generic.Queue(Of Double) Public Function MovingQueue(ByVal currentValue As Double) As Object Dim removedValue As Double = 0 If queue.Count >= queueLength Then removedValue = queue.Dequeue() End If queueSum += currentValue queueSum -= removedValue queue.Enqueue(currentValue) If queue.Count < queueLength Then Return Nothing ElseIf queue.Count = queueLength And queueFull = False Then queueFull = True Return queueSum / queueLength Else Return (currentValue - removedValue) / queueLength End If End Function
-
将移动平均销售值添加成线条。
移动平均的数据值计算将基于由 MovingQueue 自定义代码函数返回的值使用 RunningValue 函数。MovingQueue 函数将为累计的 RunningValue 运算计算调整值。通过使用以下代码来实现这一点。
=RunningValue(Code.MovingQueue(Fields!TotalDue.Value), Sum, "SalesPerson")
注意 要在一个图表内运行多个移动平均计算,必须确定在系列结尾重置队列的方式,或使用多个队列。例如,您可以使用队列(这些队列基于系列组值进行索引,并作为附加参数传递给 MovingQueue 函数)的散列表。
注意 一个图表不能跨过多个页面。因此,需将变量声明为专用非共享变量。
若要在另一个跨过多个页面的数据区域(例如列表、表格或矩阵)中使用移动平均计算,必须将变量声明为共享(即,静态)变量以越过分页维持状态。然而,由于此时使用静态变量,如果两个人同时运行报表,将存在其中一个人破坏另一个人的变量状态的可能性,但这种可能性很小。如果您需要彻底回避这种可能性,可基于请求用户的 ID (=Globals!UserID) 将每个共享变量创建到一个散列表中。
自定义图表调色板和图例
图表使用有 10 到 16 种不同色彩的预定义内置调色板。从 Reporting Services 2000 Service Pack 1 (SP1) 开始,您就可以覆盖默认色彩。若要将色彩值指定为常量或基于表达式的值,请单击外观属性上的“系列样式”按钮以获取“编辑图表值”对话框中的数据值。例如,您可以通过此操作来基于特定条件(如当前系列中的最小值或最大值)突出显示值。
注意 如果您不想定义一个完整的自定义调色板,您可以覆盖单个数据点的色彩。请使用表达式返回一个具体色彩值(以便覆盖)或返回“空值”,这样可从底层内置调色板拾取当前色彩。
例如,您想以红色突出显示 y 值为负数的所有数据点值。对于所有其他数据点,您希望使用默认色彩显示。为此,请选择“编辑数据值”并单击“外观”选项卡。单击“系列样式”按钮以打开“样式属性”对话框。单击“填充”选项卡。在填充色彩样式属性中输入以下表达式。
=iif(Sum(Fields!Sales.Value - Fields!Cost.Value) < 0, "Red", Nothing))
注意 如果您将填充色彩设置为一个常量值,此色彩会应用于具体数据系列的所有数据点。
图表图例使用色彩字段将图例项匹配到可见数据点。图例只能对每个图例项(数据系列)显示一个色彩字段;因此,它显示系列内第一个数据点的色彩。在您使用表达式动态确定系列内单个数据点色彩时请牢记这一点;即图例项总是显示第一个数据点的实际色彩。
虽然内置于 Reporting Services 图表中的图例易于使用,但是缺乏灵活性。例如,图例会占用图表内的空间。如果将图例置于绘图区外,在图例有所增长时,图表绘图区的尺寸会相应地缩小。
您可以通过使用图表或矩阵生成您自己的自定义图例来获得更大的灵活性以更好地控制图例。若要使图表中的色彩与自定义图例同步,最简单的方法是定义您自己的自定义图表调色板。CustomColorPalette 示例报表实现了一个自定义调色板和一个自定义图例。请参见图 14。
创建自定义调色板
-
定义图表系列组和类别组。
默认情况下,每个图表数据系列都分配有一种色彩。此色彩基于选中的图表调色板。在本例中,我们要基于系列组实例值覆盖这些色彩。
-
定义自定义调色板并添加自定义代码。
colorPalette 变量存储自定义调色板的定义,该调色板有 15 种不同的色彩。count 变量跟踪不同分组值的总数,以便在超出自定义调色板中的不同色彩数时回绕。mapping 散列表跟踪分组值和色彩之间的映射。这保证了同一数据系列中的所有数据点具有相同的色彩。随后,该表用于同步自定义图例色彩与图表色彩。将以下代码加入报表的自定义代码窗口。
Private colorPalette As String() = {"Green", "Blue", "Red", "Orange", "Aqua", "Teal", "Gold", "RoyalBlue", "MistyRose", "LightGreen", "LemonChiffon", "LightSteelBlue", "#F1E7D6", "#E16C56", "#CFBA9B"} Private count As Integer = 0 Private mapping As New System.Collections.Hashtable() Public Function GetColor(ByVal groupingValue As String) As String If mapping.ContainsKey(groupingValue) Then Return mapping(groupingValue) End If Dim c As String = colorPalette(count Mod colorPalette.Length) count = count + 1 mapping.Add(groupingValue, c) Return c End Function
-
调用 GetColor() 函数将色彩分配给数据点。
从填充色彩样式属性调用 GetColor 函数。编辑数据值以打开“编辑图表值”对话框并单击“外观”选项卡(图 15)。单击“系列样式”按钮并单击“填充”选项卡。当前系列组值将作为参数传递给 GetColor 函数,将内部组实例值映射到色彩值时需要此函数。
图 15. 指定明确的数据系列样式注意 如果存在多个图表系列组,您可以连接系列组值以创建一个在 GetColor 函数中使用的唯一标识符。以下代码是一个示例。
=Code.GetColor(Fields!Country.Value & "|" & Fields!City.Value)
-
添加图表图例。
您可以使用内置的图表图例。或者,也可关闭内置的图表图例并按照下一操作过程中的步骤创建您自己的、有一个表格或一个矩阵数据区的自定义图表图例。
创建自定义图例
-
将一个表格数据区域添加到报表。
将表格放到图表旁并将其绑定到与图表相同的数据集。
-
通过添加表格组在表格中反映图表分组结构。
如果图表使用系列分组,则通过添加基于与图表系列组中组表达式相同的表格组来将它们添加到表格。然后添加图表类别分组(如果存在)作为内部表格组。
一般而言,如果图表有 m 个系列分组和 n 个类别分组,则您需要为自定义图例添加 m+n 个表格组。
对于各个表格组,请确定只显示组头(它将包含图例说明)。另外,请删除表详细信息行,除非您要使用表详细信息行来模拟图表数据表。
-
设计自定义图例。
为自定义图例的色彩字段添加一个矩形。例如,您可能会将其添加到第一个表格列。如步骤 2 所示,您将在表格中只有组头行。矩形将加到最内部的组头层级。
将矩形的 BackgroundColor 属性设置为与用于图表数据点的填充色彩的表达式等价的表达式。在最普通的情况下,该表达式会像以下代码中一样仅包含一个分组值。
=Code.GetColor(Fields!Country.Value)
对于图例文本,可使用与类别及系列组/标签表达式中相同的表达式,或进行实验直到获得您所要的图例说明文本。
饼图和圆环图
数据点标签和图例标签部分说明了如何为饼图扇面设置内部和外部数据点标签。本节说明了饼图和圆环图的几个附加属性。
与其他图表类型不同,饼图或圆环图只有一个“分组维度”(即,一个数据系列)。若要在饼图和圆环图中使用两个维度,就需要将图表相互堆叠。
注意 在发布报表期间,Reporting Services 自动地将饼图或圆环图的系列组转换成类别组以将数据显示成一个数据系列。
饼图常用于显示数据点的相对百分数。一般而言,百分比值(例如,可将其显示成数据点标签)可通过用数据点值表达式除以整个图表的总和来计算。以下代码是一个相应的示例。
=Sum(Fields!Sales.Value) / Sum(Fields!Sales.Value, "SalesChart")
如图 16 所示,表达式被作为一个数据标签表达式添加。
默认情况下,饼图扇面使用了黑色边框以增强其可见性。但是,如在下一节的图 17 所示,您可以使用特定的数据点外观设置覆盖边框。如果您希望边框的色彩与饼图扇面的色彩一致,您必须使用上一节所述的自定义调色板。使用自定义调色板,您可以通过调用基于类别分组值分配色彩的自定义代码函数为数据点(饼图扇面)设置与边框相同的色彩。
添加图表数据表
您可能希望将明细数据添加到图表中。直接将数据添加到图表可能会使图表更难以理解。可代之以数据表形式添加相应信息。
图表是可用来形象化值的总体分布及识别关注区域(如,非常大及非常小的值)的一种非常有效的方法。读者可能希望根据底层明细数据更详尽地分析信息。
Reporting Services 提供以下三种方式在图表数据点上添加互动操作:
-
使用“跳至报表”操作可基于当前系列/类别组值,通过将这些值作为钻取参数值添加,钻取到其他报表来显示明细数据。
-
使用“跳至书签”操作可跳至同一报表中的某部分(如数据表部分)。
-
使用“跳至 URL”操作可生成一个指向报表外的外部导航目标的超链接。
图 17 显示了包含在本白皮书中的 PiePercentage 示例报表的简化版本。
创建数据表格
-
创建有自定义调色板的饼图。
图 17 中的饼图有两个类别分组。外部分组以订单年度为基础。内部分组以产品类别为基础。定义自定义调色板,如在创建自定义调色板步骤中的步骤 2 所述。
由于存在两个类别分组,我们使用以下表达式生成一个复合键,该键将被传递给 GetColor 函数。
=Code.GetColor(Fields!OrderYear.Value & Fields!ProdCat.Value)
如果我们对数据点填充色彩属性和数据点边框色彩属性应用相同的 GetColor 函数调用,饼图扇面将不显示默认的黑色边框。
-
仅为那些代表大于全部饼图 4% 的饼图扇面显示数据点标签。
要实现这一目的,请将以下函数添加到报表自定义代码部分。重要的是为那些没有标签的饼图扇面返回空白标签字符串,否则图表控件将为该扇面显示默认标签。(默认标签是底层数据点值。)
Public Function GetLabel(ByVal currentValue As Double, ByVal totalValue As Double) As String If currentValue / totalValue < 0.04 Then Return " " Else Return Format(currentValue / totalValue, "P1") End If End Function
数据点的标签表达式调用 GetLabel 函数以计算百分比值/标签。
=Code.GetLabel(Sum(Fields!Sales.Value), Sum(Fields!Sales.Value, "SalesChart"))
-
为图表创建数据表(矩阵)。
图表仅按年度和产品类别显示聚合的销售数据。在数据矩阵中,我们希望以同样的方式对数据进行分组。
我们添加矩阵数据区域并将其绑定到与图表相同的数据集。然后,我们将 OrderYear 和 ProdCat 字段作为行组添加到矩阵上,并在矩阵单元中聚合 Sales 值。若要添加小计,可右键单击矩阵中的组头单元格并从上下文菜单中选择“小计”。
注意 单击矩阵标题中的绿色小三角形,选择“小计”属性,在这里您可以为小计单元格明确设置样式属性。例如,您可以用不同的背景色彩显示小计单元格以从视觉上与其他数据相区别。
底层数据集提供了比图表中显示粒度更为细的数据粒度。我们可基于子类别添加另一个(内部)行组,以及添加季度和月份作为矩阵的列组来利用数据矩阵的优势。
-
将切换明细添加到数据矩阵中。
要为特定组添加明细效果,请右键单击组头并编辑组属性。在“组属性”对话框的“可见性”选项卡上,选择应切换当前组可见性的报表项名称。通常在父组中选择文本框的报表项名称。如果不存在父组,可将文本框添加到矩阵角中,并将其用于在矩阵中关联最外面的分组级别。
设置切换状态的“初始可见性”选项也在组的“可见性”选项卡上进行设置。将此选项设置为“可见”意味着初始状态为展开,而设置为“隐藏”意味着初始状态为折叠。
-
调整初始切换图像和切换状态。
如果组的初始切换可见性状态设置为“可见”,则切换该组的报表项上的切换图像可能显示一个加号 (+)。相反,要显示减号 (-) 切换图像,请右键单击切换组的报表项;这通常是父组头中的文本框。在上下文菜单中选择“属性”。在“文本框属性”对话框中,选择“可见性”选项卡并设置切换图像的初始外观。因为组切换可见性设置为“可见”,所以文本框的初始外观应设置为“展开 (-)”,如图 18 所示。
图 18 调整切换图像的初始外观 -
添加书签以连接图表和数据矩阵(可选)。
启用从图表数据点到数据矩阵的书签导航可分为两步。第一步,在数据矩阵内部定义书签 ID。第二步,定义图表数据点上书签导航操作的跳转。
要在数据矩阵中添加书签,请右键单击 order year 行组的组头,并在上下文菜单中选择“属性”。在“文本框属性”对话框中,选择“导航”选项卡,并使用生成字符串的表达式定义“书签 ID”表达式:=CStr(Fields!OrderYear.Value)。这为我们提供书签跳转目标。
打开“图表属性”对话框,编辑数据点属性。在“操作”选项卡上,选择“跳至书签”,使用同样用于书签 ID 的表达式。
散点图和气泡图
散点图和气泡图与其他图表类型不同,因为它们具有明确的数据点 x 值,而不是将类别分组值用作 x 值。因此,可对数据进行分组(和聚合),将其分为不同的类别而不是显示在 x 轴上的值。例如,若要在 x 轴上显示各销售人员去年的销售量,如果两个销售人员拥有相同的 x 值,则不希望聚合 y 轴上的值。BubbleChart 示例报表(图 19)具有基于销售人员的类别分组,所以该报表聚合每个销售人员的销售数据。但在 x 轴上显示的是去年的销售值。
注意 了解数据点的 x 值属性和基于系列组与类别组的图表数据分组之间的区别非常重要。如果设计散点图或气泡图,并且该图在预览中仅显示一个数据点,但是本应该显示很多不同的点,则最有可能的解释就是未定义任何的类别组或系列组。如果未对类别组和系列组进行定义,则基础数据集行聚合为具有特定 x 值和 y 值的一个数据点。在某些情况下,您可以定义与 x 值表达式相同的类别组或系列组表达式,但是在散点图或气泡图中,您更想根据数据所表示的内容添加类别或系列分组。如果图表为每位销售人员聚合销售值,类别或系列分组应以 BubbleChart 示例报表中显示的销售人员 ID 或姓名为依据。
StepFunctionChart 示例报表给出了散点图的另一用途。散点折线图基于测量 ID 使用类别分组。对于特定的几天(显示为 x 值),在数据集中存在多个测量值(沿 y 轴上显示),从而导致竖直递增。
表内嵌图
有时,运行时的数据量未知,并且希望动态“增长”图表的大小。实现此目的一个方法是在另一个数据区域的组中嵌入一个图表。例如,可以根据下列表达式,在列表数据区域使用详细信息组:
=Ceiling(RowNumber(Nothing)/20)
这将每二十个详细信息行分为一组。将图表嵌入到该列表中可在运行时每二十行创建一个图表实例。
要创建数据的内嵌条形图可视化,可使用两种不同的方法实现:
-
嵌入一个已动态算出右填充量的图像(参见图 21)。调整静态图像的右填充量可获得动态伸展图像所得的条形图效果。
-
嵌入一个具有已算出的 y 轴最大值的条形图(参见图 23)。
-
这两种方法在 TableInlineCharts 示例报表中并行实现。
若要基于内嵌的图像实现内嵌可视化
-
设计将用于“条形图”可视化的内嵌图像。
通常,简单的梯度图像可一目了然。将作为内嵌图像的图像添加到报表中。
-
将表格添加到组并将数据可视化。
设计表格的分组结构。可以将图像可视化置于组头中或置于表格详细信息行中。在新的表格列中添加内嵌图像。从步骤 1 中选择内嵌图像作为图像源。
-
计算右(或左)图像填充量。
为图像报表项的右(或左)填充量属性创建表达式。表达式将要进行可视化的数值除以最大值。然后将相对大小乘以步骤 2 中定义的表格列的宽度。必要时,您可能也想通过使用 Math.Min 或 Math.Max 函数将填充量限制在特定范围内。
在 TableInlineChart 示例报表中,表格列的宽度为 2 英寸。对于填充量计算,我们使用点测量单位,每英寸包括 72 个点。因此,假设将左填充量设置为 0 点,则“右填充量”选项的范围是在 144 点之内。使用下列代码设置填充量。
=144 * (1.0 - Fields!UnitsInStock.Value / Max(Fields!UnitsInStock.Value, "DataSet1")) & "pt"
图 22 动态计算图像右填充量大小 -
将图像大小属性设置为“合适”。
在前一步骤中定义的填充量决定可用于图像伸展的空间的大小,从而生成条形图可视化效果。
内嵌图像方法的一个缺点是如果在图像中使用字体或细线,图像可能会拉长。使用图表进行内嵌可视化(图 23)可提供更多对可视化的控制,并通常能获得更佳效果。
若要使用图表进行内嵌可视化
-
添加表格以分组数据。
设计表格的分组结构。请记住,只能将图表置于表格表头或表格表尾中或置于组头或组尾中。
-
添加新的表格列并将图表置于组头中。
要将条形图可视化的图表绘图区调整至最大尺寸,请应用下列图表属性设置。
常规设置:设置为条形图,关闭图例。
数据:将图表与父表绑定到相同的数据集;基于要进行可视化的值添加数据点值。
X 轴:关闭轴标签,关闭网格线,将刻度线设置为“无”。
Y 轴:关闭轴标签,将刻度线设置为“无”,将最大值设置为在包含表格或数据集范围内计算出的最大值。要实现正确的条形图大小需要如此设置,否则每个图表实例将基于特定组的数据值仅自动缩放 y 轴。
-
优化图表可视化(可选)。
尝试调整图表绘图区样式设置、y 轴的主要网格线、3D 效果或动态颜色设置,以进一步优化内嵌图表可视化。
图表可扩展性和手动创建图表
本白皮书提供通过表达式和自定义代码功能调整图表设置和扩展现有图表功能方面的信息。除此之外,还有其他方法可将更多高级图表功能集成到 Reporting Service 中:
-
集成由自定义程序集生成的表格图像。
-
基于 Reporting Services 2005 的新“自定义报表项”功能实现图表可扩展性。
-
使用可提供增强表格功能的第三方 Reporting Services 2005 加载项组件。这些都基于自定义报表项功能。
若要集成由自定义程序集生成的图像
-
设计和实现自定义程序集以生成图像。
自定义程序集必须自行检索数据、负责分组/排序数据以及生成图表图像。
注意 自定义程序集必须以 byte[] 形式返回图像。不能以 System.Drawing.Image 形式返回图像。通常可使用与下列代码相似的代码转换 System.Drawing.Image 对象。
System.IO.MemoryStream renderedImage = new MemoryStream(); myChart.Save(renderedImage); renderedImage.Position = 0; return renderedImage.ToArray();
-
将图像添加到报表中。
将图像类型设置为“数据库”。如果生成的图像是 PNG 图像格式的位图,则将图像的“mimetype”属性设置为“image/png”。对于图像值属性,请使用与下列表达式类似的表达式。
=MyCustomAssembly.GenerateChart()
-
在“报表设计器预览”视图中查看报表以确认报表工作正常。
注意 在默认配置中,自定义程序集在“报表设计器”预览中以“FullTrust”运行。因此,对于要求特定代码访问安全权限的操作(如文件输入/输出、数据提供访问等等),在“FullTrust”下将自动为它们授予这些权限。
-
在报表服务器上部署自定义程序集。
确保报表服务器的安全策略配置在运行时为自定义程序集授予足够的权限,否则图像生成将失败。有关详细信息,请参阅 SQL Server 2005 联机丛书中的 Understanding Code Access Security in Reporting Services(英文)。
基于 CustomReportItem 的图表与自定义程序集的比较
通过自定义程序集方法使用自定义报表项有多项益处。首先,可以构建自己的、可直接集成到“报表设计器”的设计时支持组件。其次,在运行时,可利用 Reporting Services 处理引擎来检索数据、应用分组/排序和筛选器。CustomReportItem 运行时控件访问经过处理的数据,并根据交互图像映射和关联操作生成图表图像。
在构建自己的 CustomReportItem 之前,请仔细学习、研究文档和示例。下列站点提供更多相关信息:
-
SQL Server 2005 联机丛书的自定义报表项示例
-
有关自定义报表项的信息,请参阅 MSDN 上的 Chris Hays's Reporting Services Sleazy Hacks Weblog(英文)。