基于数据仓库上的 SSRS 报表展示,一般可以直接通过 SQL 查询,存储过程,视图或者表等多种方式将数据加载并呈现在报表中。但是如果是基于 Cube 多维数据集的数据查询,就不能再使用 SQL 的语法了而应该使用 MDX 查询。关于 MDX 和其它 SSRS 的文章,请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)

这是我们要实现的报表效果,使用的数据库示例是 MDX Step by Step 2008 的 SSAS DEMO 数据库。

收起的效果 - 按 Product Category 分组聚合并呈现所有财年的销售情况。

展开之后能够看到 Product Sub Category 的销售数据。

通俗一点的讲,从数据仓库到 Cube 的过程就是将平面数据立体化的过程,在这个过程中加入了从各个不同角度对数据的聚合。而从 Cube 到 SSRS 报表的过程又恰恰相反将立体化的数据平面化的结果。

分析上面的需求,其中需要娶到 Category, Subcategory, Calendar Year 以及 Reseller Sales Amount 的数据。SSRS 报表可以对平面化的数据非常快速的分组和聚合的,因此上面的需求我们整理一下就得到了这样的一个数据表原型。

假设我们基于上面的这张表去创建 Cube 的话,很显然 Cube Dimension 应该包含了 Product 和 Date 这两个维度和一个度量值维度- Reseller Sales Amount , 每一个维度的属性层次结构可以理解为在 Cube 空间中的一个轴。因此,Product 这个维度至少可以分出 Product Category 和 Product Sub Category 这两个属性层次结构,Date 维度我们假设这里只有 Calendar Year 这一个属性层次结构,那么再加上度量值维度就构成了 Cube 空间的四条轴,四条轴交汇定位到空间的一个点,这个点是一个单元格,它包含了 Reseller Sales Amount 的值。

这是从 Cube 空间体的角度来阐述从 DW 到 Cube 的变化,但是 SSRS 中表格数据中只支持二维数据组合,因此要把 Cube 空间的立体数据给拉平了再呈现回来。

创建报表并新建数据源,这时的数据源连接方式与之前连接到 SQL Server 数据库不同,连接的是 Analysis Services 。

新建 Dataset,并且指定刚才新建的数据源。因为数据源的改变,SSRS Dataset 中的 Query Designer 将对应的调整为支持 MDX 查询的界面。

Query Designer 中可以看到指定的分析服务的数据库以及各维度,层次结构和度量值组。

也支持编写 MDX 查询代码,特别是比较复杂的业务逻辑的情况下,就需要自己手工编写 MDX 查询。

我们直接使用拖拽的方式快速开发一个 MDX 查询报表,并且现在要做的就是让这几条轴交汇一下来展现我们扁平化的数据表原型。记住:每一个属性层次结构就是一条轴,包括度量值维度。这几条空间里的轴在数据表上最直接的反映就是形成列,每一条数据的形成就是四条轴在 Cube 空间交互的结果。

保存之后,看到 MDX 查询已经自动编写好了。

整理一下格式,看看这个 MDX 查询是怎么写的。

 SELECT NON EMPTY { [Measures].[Reseller Sales Amount] } ON COLUMNS,
NON EMPTY {
(
[Product].[Category].[Category].ALLMEMBERS *
[Product].[Subcategory].[Subcategory].ALLMEMBERS *
[Date].[Calendar Year].[Calendar Year].ALLMEMBERS
)
} DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS
FROM [Chapter 3 Cube]
CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING,
FONT_NAME, FONT_SIZE, FONT_FLAGS

去掉一些不关键的属性,简化一下就是 -

SELECT NON EMPTY { [Measures].[Reseller Sales Amount] } ON COLUMNS,
NON EMPTY {
(
[Product].[Category].[Category].ALLMEMBERS *
[Product].[Subcategory].[Subcategory].ALLMEMBERS *
[Date].[Calendar Year].[Calendar Year].ALLMEMBERS
)
}ON ROWS
FROM [Chapter 3 Cube]

与前面在 Query Designer 上的查询结果相比一下这个 MDX 查询在 SQL Server Analysis Service 上面的查询结果,Query Desinger 的查询结果扁平化了,更容易理解成一个普通的数据表。而在这里,列头的构成比较复杂,它本想描述一下这种立体的感觉,无奈在二维平面的世界里它也描述不出来。

注意,这里的 MDX 查询在 Columns 只能是 Measure 度量值。

创建好了 Dataset 之后就可以直接使用这个平面数据了,拖放一个 Matrix 过来。Matrix 和 Tabular 最大的区别就是 Matrix 可以直接形成行和列的聚合。

对比一下拖放之间和拖放之后的 Matrix 格局便于大家理解。Row 上放的应该是 Category 和 Subcategory,列上放的应该是 Calendar Year,被聚合的数据应该是 Reseller Sales Amount。但是这里,我们先放 Subcategory,然后在 Subcategory 基础上添加 Group - Category。

选中 Subcategory 然后右键添加一个 Group - Parent Group 。

Product Subcategory 可以按照 Category 分组,添加一个 Group Header。

添加完了之后的效果,不过接着就应该删除 Category 这一列,让 Category 这个元素显示在 Subcategory 元素之上,并且添加 Category 级别对 Reseller Sales Amount 的 SUM 聚合。

对比上下这种变化,基本上就完成了报表数据的分组聚合设计。

接着调整一下,然后上色。

为 Category 添加一个 Total。

基于 Calendary Year 也添加一个 Total。

添加完了后上上色,调整一下格式。

将标题 Subcategory 改成 Category ,并且设置 Subcategory 按照 Category 的点击来展现收缩和展开效果。

自此就已经完成了一个简单的 MDX 查询在 SSRS 报表上的展现。不过,如果要添加参数比如希望能够先筛选 Category 和 Subcategory,回到 Dataset 里的 Query Designer 添加 Parameter 。

保存并刷新,SSRS 为自动创建两个参数。

预览报表并选择参数。

最后查询出来的结果。


分析一下在 Query Builder 中的 MDX 查询语句,我就以 MDX Step By Step 上面的例子来说吧,这个 MDX 查询在 SSAS 查询分析器中没有问题。

但是同样的语句放到 Query Designer 中就有问题,提示这种错误。

The query cannot be prepared: The query must have at least one axis. The first axis of the query should not have multiple hierarchies, nor should it reference any dimension other than the Measures dimension.. Parameter name: mdx (MDXQueryGenerator)

其中要注意的有三点:

  • MDX Query 必须至少要有一个轴。
  • 第一个轴中的查询不能包含多个层次结构,只能有一个。
  • 除了 度量值维度 Measure Dimension 之外不能引用其它任何的维度。

再来看看我们的带参数的 MDX 查询语句。

SELECT  -- COLUMNS 轴也就是第一个轴上的维度是度量值维度
NON EMPTY { [Measures].[Reseller Sales Amount] } ON COLUMNS,
-- ROWS 上由多个属性层次结构的成员构成 SET 集
NON EMPTY {
(
[Product].[Category].[Category].ALLMEMBERS *
[Product].[Subcategory].[Subcategory].ALLMEMBERS *
[Date].[Calendar Year].[Calendar Year].ALLMEMBERS
)
}ON ROWS
FROM
-- 第三层查询基于前两层查询筛选之后的结果
(
-- 第二层查询,根据参数 Product Subcategory 并根据第一层查询的结果返回相应的 Subcategory
SELECT ( STRTOSET(@ProductSubcategory, CONSTRAINED) ) ON COLUMNS
FROM (
-- 第一层查询,根据参数 Product Category 决定了这个层次结构上所有的 Category 成员
SELECT ( STRTOSET(@ProductCategory, CONSTRAINED) ) ON COLUMNS
FROM [Chapter 3 Cube]
)
)

其中最重要的就是 STRTOSET 函数的使用,在 SSRS 加载的时候会首先列出所有 Product Category 成员供我们选择。

就相当于 -

选择完了 Bikes 之后,Subcategory 刷新完然后选择 Mountain Bikes 和 Road Bikes

就相当于

整个 MDX 查询翻译到 SSAS 的就是

对比一下最终在 SSRS 上的报表,相当于把 MDX Query 的结果给扁平化了。

要注意的是 STRTOSET('[Product].[Category].[Bikes]', CONSTRAINED),它将一个字符串转化成了一个具体的集合,并且加上了 CONSTRAINED 那么在第一个参数字符串中就只能指定具体的层次结构中的成员,它有一个限定作用。

比如说如果想直接写上全部成员就会发生这样的错误。

The restrictions imposed by the CONSTRAINED flag in the STRTOSET function were violated.

如果去掉 CONSTRAINED,那么结果是OK的,类似的函数还有 STRTOMEMBER.

至于 STRTOSET 和 STRTOMEMBER 这两个 MDX 函数就不在这篇 SSRS 博客中详细解释了,这里只是通过例子对比大概描述了 MDX 查询参数化的过程和在 SSRS 报表上的使用。这种方式非常直接也比较简单,当然也还有其它传参数的方式,比如编写 Expression 等等在不同的场景下也会使用到。

没有写好的地方,欢迎大家积极补充和指正!


更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)

如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。


微软BI 之SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表的更多相关文章

  1. SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表

    SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表 SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表 2013-10-09 23:09 by BI Wor ...

  2. 微软BI 之SSRS 系列 - 基于时间段参数的 MDX 查询以及时间日历 Date Picker 的时间类型参数化

    今天在天善问答里看到一个问题,如果我没有理解错的话,它应该是指比如在一个报表中选取一个时间段,然后求出这个时间段的某个 Measure 的 SUM 和.并且同时求出这两个时间点对应的上一年的时间点之间 ...

  3. 微软BI 之SSRS 系列 - 在 Cube 中通过 MDX 查询实现基于父子递归关系的汇总报表

    之前我写了一篇在 SSRS 开发中处理这种父子关系的汇总与聚合的文章 (SSRS 系列 - 使用分组 Group 属性实现基于父子递归关系的汇总报表),示例中的查询是基于 SQL Server 关系型 ...

  4. 微软BI 之SSRS 系列 - 如何在 MDX 查询中获取有效的 MEMBER 成员属性作为参数传递

    这篇小文章的来源是 天善问答,比如在报表中要根据点击某一个成员名称然后作为参数传递给自身报表或者下一张报表,这个在普通的 SQL 查询中没有任何问题.但是在 MDX 中查询是有区别的,比如在 MDX ...

  5. 微软BI 之SSRS 系列 - 报表邮件订阅中 SMTP 服务器匿名访问与 Windows验证, 以及如何成功订阅报表的实例

    这篇文章源于在上一篇博文中有园友提出订阅 SSRS 报表时的一个问题,  于是就好好总结了一下,把有关 SSRS 报表订阅的要点和容易出现问题的地方写出来,希望对大家有所帮助! 参看上一篇博文 - S ...

  6. 微软BI 之SSRS 系列 - 使用 LookupSet 和 Adjacent Group 等高级技巧在报表中跨 Dataset 分组查询

    SSRS 报表中有一些高级的技巧,平常很少用到,下面我通过这个案例来展现一下如何在实际开发中使用它们,并且如何解决一些实际的需求. 这张报表分别统计了不同的 Product 产品在不同的月份的 Ord ...

  7. 微软BI 之SSRS 系列 - 使用分组 Group 属性实现基于父子递归关系的汇总报表

    基于父子关系的递归结构在公司组织结构里比较常见,基本上都是在一张表里实现的自引用关系.在报表中如果要实现这种效果,并且在这个基础上做一些数据的汇总,可以使用到下面提到的方法. 要实现的效果大致如下 - ...

  8. 微软BI 之SSRS 系列 - 报表中分组聚合中处理不规则层次结构的技巧(没有子元素的时候不展开, 删除+符号)

    分组聚合的展开和收起效果在SSRS Report中非常常用,并且有时还要处理一些比较特别的情况.比如分组合并时有的层次结构是不规则的,有的组有两层,遇到这种情况应该如何处理?   注意到下面的这个需求 ...

  9. 微软BI 之SSRS 系列 - 实现 Excel 中图表结合的报表设计

    来自群里面讨论的一个问题,EXCEL 中有类似于这样的图形,上面是 Chart, Chart X轴上的值正好就是下方 Table 的列头,这个在 SSRS 中应该如何实现?   SSRS 2008.2 ...

随机推荐

  1. indy10的idhttpServer应答字符串

    indy10的idhttpServer应答字符串 先看应答字符串的代码: procedure TIdIOHandler.Write(const AOut: string; AByteEncoding: ...

  2. git推送tag到远端服务器

    git推送tag到远端服务器 默认情况下,git push并不会把tag标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库.1.push单个tag,命令格式为:git push origi ...

  3. 看书小记6(《C专家编程》)

    typedef不常见但值得一提的用途: 1. 用typedef来定义与平台无关的类型. 比方定义一个叫 REAL 的浮点类型.在目标平台一上.让它表示最高精度的类型为: typedef long do ...

  4. 替换NSUserDefaults的方案

    替换NSUserDefaults的方案 效果 源码 https://github.com/YouXianMing/iOS-Utilities // // BaseValueStorageManager ...

  5. Redis在Mac下的安装与使用方法

    首先从Redis官网http://www.redis.io去下载最新版本的Redis安装文件(此处以Redis版本为例进行说明).   Redis 2.6.16版本的下载地址:http://downl ...

  6. 对于多个button要在同一个监听器中实现自己的单击事件的方法小诀窍。

    在网上的一些教程中往往是把一个button加入多个监听器,却非常少有人会把多个button在同一个监听器中去实现他们的单击事件,并且这杨的事实上是非常有用的,比方说在制作一个简单的计算器是就须要0-9 ...

  7. python读取yaml配置文件

    支持多种语言:python.js.golang.java.c.c++ YAML 语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写.它实质上是一种通用的数据串行化格式. 它的基本语法规则如下 ...

  8. Django的事务性

    Django默认是自动提交的 使用TestCase的每个test用例都是事务性的.如果不想这样,可以使用TransactionTestCase 替代 Django自动保证delete()/update ...

  9. Verilog 加法器和减法器(2)

    类似半加器和全加器,也有半减器和全减器. 半减器只考虑当前两位二进制数相减,输出为差以及是否向高位借位,而全减器还要考虑当前位的低位是否曾有借位.它们的真值表如下: 对半减器,diff = x ^y, ...

  10. 深度学习Github排名,很不错的介绍

    今天看到这篇文章,把深度学习github排名靠前的项目,介绍了一下,很不错: https://blog.csdn.net/yH0VLDe8VG8ep9VGe/article/details/81611 ...