SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表
SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表
SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表
2013-10-09 23:09 by BI Work, 184 阅读, 0 评论,收藏, 编辑
基于数据仓库上的 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 等等在不同的场景下也会使用到。
没有写好的地方,欢迎大家积极补充和指正!
SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表的更多相关文章
- 微软BI 之SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表
基于数据仓库上的 SSRS 报表展示,一般可以直接通过 SQL 查询,存储过程,视图或者表等多种方式将数据加载并呈现在报表中.但是如果是基于 Cube 多维数据集的数据查询,就不能再使用 SQL 的语 ...
- 微软BI 之SSRS 系列 - 基于时间段参数的 MDX 查询以及时间日历 Date Picker 的时间类型参数化
今天在天善问答里看到一个问题,如果我没有理解错的话,它应该是指比如在一个报表中选取一个时间段,然后求出这个时间段的某个 Measure 的 SUM 和.并且同时求出这两个时间点对应的上一年的时间点之间 ...
- 微软BI 之SSRS 系列 - 在 Cube 中通过 MDX 查询实现基于父子递归关系的汇总报表
之前我写了一篇在 SSRS 开发中处理这种父子关系的汇总与聚合的文章 (SSRS 系列 - 使用分组 Group 属性实现基于父子递归关系的汇总报表),示例中的查询是基于 SQL Server 关系型 ...
- [译]SSRS 编写带参数的MDX报表
编写MDX报表长久以来对于报表人员来说都比较痛苦. 当然如果你用查询设计器(Query Designer) 直接拖拉数据集那就很方便,但是你们有没有想过查询设计器是怎么创建MDX的.或者创建的参数是如 ...
- Oracle,Mysql ,SQL Server 三大数据库带参数的模糊查询, 拼接查询条件问题
最近项目开发一直在不断切换数据库,有时候一条sql 要同时考虑多种数据库中的兼容问题 , 先总结一条模糊查询拼接查询条件的问题,后续追加总结. 目前使用 mybatis: 1. Oracle 中使 ...
- hibernate学习系列-----(5)hibernate基本查询下篇:hibernate聚合函数、分组查询及命名查询
在上一篇中,大致学习了hibernate的基本查询:HQL基本查询,今天,继续昨天的步伐,继续学习hibernate的基本查询..... 1.hql聚合函数,先大致列一下hql的聚合函数有哪些吧: 在 ...
- vue 带参数的跳转-完成一个功能之后 之后需要深思,否则还会忘记
我要写详细点,否则下次很容易忘记 写了一个页面,这个页面里面添加了 很多a 标签,跳转都是同一个页面,内容不一样,方法 首先 路由 设定好 routes:[ { path:'/aaa', name:' ...
- Python带参数的装饰器
在装饰器函数里传入参数 # -*- coding: utf-8 -*- # 2017/12/2 21:38 # 这不是什么黑魔法,你只需要让包装器传递参数: def a_decorator_passi ...
- Qt带参数的信号和槽
在Qt的开发过程中,信号带参数是很常见的,在使用带参数的信号槽时,有以下几点需要注意. 当信号和槽函数的参数数量相同时,它们的参数类型要完全一致. 信号和槽函数的声明: signals: void i ...
随机推荐
- hdu To and Fro
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1200 代码: #include <stdio.h> #include <string ...
- cmd介面,进adb命令提示符error
有几个操作的电话系统测试,需要输入adb命令时出现了头疼的事,当输入命令,一个直接报执行:error 推荐处理的方法: 1.当然就是关机重新启动.之前我是这样,挺麻烦.必进在win7上输入命令费时间. ...
- Hibernate_10_继承的例子_单表
只是建一个表,所有属性都包括在此表.使用discriminator 到父和子类之间的区别. 1)父类(Article): public class Article { private Integer ...
- 第一个JavaWeb程序
转载 第一个JavaWeb程序 JavaWeb学习总结第二篇—第一个JavaWeb程序 最近我在学院工作室学习并加入到研究生的项目中,在学长学姐的带领下,进入项目实践中,为该项目实现一个框架(用已有框 ...
- python解析命令行
可以解析这样的命令 ./cron_ctrl jobname1 --stop ;./cron_ctrl jobname1 --start;./cron_ctrl jobname1 --list #!/u ...
- 我的MYSQL学习心得(十二)
原文:我的MYSQL学习心得(十二) 我的MYSQL学习心得(十二) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYS ...
- 调试经验--硬盘U菜
调试经验--硬盘U菜 随着嵌入式设备功能的开发,随着对存储设备的需求:需要存储大量数据信息.需要在转储数据,U盘升级功能等. 在使用存储设备的过程中,我们遇到一些问题,也总结了些经验: 1.几 ...
- CORS跨域资源共享
CORS(跨域资源共享)跨域问题及解决 当使用ajax跨域请求时,浏览器报错:XmlHttpRequest error: Origin null is not allowed by Access-Co ...
- PHP 2:从一个实例介绍学习方法
原文:PHP 2:从一个实例介绍学习方法 在前面我已经描述了PHP,Apache以及MySQL的安装与配置.下面将介绍一下我如何学习PHP.首先我自己已经有了一些编程经验,就拿我自己而言,已经熟悉C/ ...
- object 插入元素,插入HTML页面
object标签用于定义一个嵌入的对象,包括:图像.音频.Java applets.ActiveX.PDF以及Flash.该标签允许您规定插入HTML文档中的对象的数据和参数,以及可用来显示和操作数据 ...