date: 2019/1/24 11:17:16
前言
之前在做报表时没有注意那么多细节,上次给甲方公司做他们总公司汇总,明细报表,这种报表动辄几十万上百万的数据量,明细表还好使用控件进行分批查询的策略可以解决问题,但是汇总,统计的报表没办法进行分批或者限制查询量进行处理,因为那样处理必定导致统计结果和表达运算不够准确。因此找到了帆软官方中对于报表性能优化的多个文章,进行仔细查看和对SQL优化处理,最后达到还算满意的查询速度。
文章内容来源自帆软官方多篇文章:如需阅读官方教程,可自行忽略本文内容,地址如下
http://help.finereport.com/category-view-230.html
一 报表执行过程
帆软报表的执行过程大致分为两步: 1、报表计算 2、页面转换
**报表计算过程:**先读取我们的sql语句,将完整的sql语句传至数据库服务器,由数据库返回数据给报表服务器,报表服务器进行数据列的扩展、汇总、关联、公式运算等计算,最终生成报表结果。
**页面转换过程:**报表计算完成后生成xml页面,需要转换为html页面才能在浏览器中查看。
帆软通过log日志进行监控这一执行过程
二 优化报表取数
1、描述
报表的核心是数据,数据集是否合理决定了报表的质量
1、每一张表都应该有一个主数据集,为了降低维护工作量,尽量将所有字段置于主数据集下。
2、在制作报表之前,尽量考虑到所有需要展示的数据字段,在数据库软件中,合理编写sql语句,大数据情况尽量对sql做优化,以及添加索引
2、取数原理
设计器拼出最终的SQL,将SQL语句传给数据库,数据库执行,将数据返回给设计器。
由于计算过程首先要通过SQL语句从数据库中取数据,我们可以通过控制数据量的大小和对数据的提前预处理来提高报表的性能。
3、优化SQL
1、SQL中取用具体的字段
2、SQL中直接分组代替报表中分组
3、SQL中直接排序代替报表中排序
4、SQL中直接过滤代替报表中过滤
报表计算过程中很多时候并不需要对表中的所有记录进行操作,而只是需要对部分满足条件的记录进行操作,虽然可以在报表设计器中对数据过滤,不过我们建议在SQL中对数据提前过滤,这样数据库返回的数据就减少了,既加快了取数速度,也加快了报表的运算速度
4、使用视图、存出过程
视图是由SELECT语句组成的查询定义的虚拟表,由一张或多张数据库实际的表中的数据组成的,从数据库系统外部来看,视图就如同一张表一样。
存储过程通过流控制与SQL语句,可以对数据进行强大的运算与处理,对于业务比较复杂的应用,常常需要将原始数据通过存储过程处理后再供报表使用。另外存储过程运行前,数据库会对其进行语法和句法的分析,并进行优化,这种已经编译好的存储过程极大地改善SQL语句的性能。在报表端也只需要书写较短的调用语句来获得结果,从而降低网络的通信量。
所以表与表的连接、复杂的SQL尽量在数据库中使用视图或者存储过程直接进行,这样将复杂的SQL语句直接保存于数据库服务器端(数据库本身会对SQL语句进行语法分析并进行优化),在报表设计器端就不需要写大段的SQL语句而是直接调用视图或存储过程了,一方面减少网络传输量,减轻数据库的压力,另一方面加快了报表的运算速度
5、优化示例
1、count(*) 比count(1)稍快
2、in和exists使用
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率
SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’ FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB’)
3、EXISTS替换DISTINCT
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换
SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X’ FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO)
4、用>=替代>
--如果DEPTNO上有一个索引
--高效:
SELECT * FROM EMP WHERE DEPTNO >=4
--低效:
SELECT * FROM EMP WHERE DEPTNO >3
两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录.
5、应避免在 where 中使用!=或<>操作符
将导致引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。
6、应避免在 where 子句中使用 or 连接!
否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10 union all select id from t where num=20
7、 in 和 not in 也要慎用!
因为IN会使系统无法使用索引,而只能直接搜索表中的数据。如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
8、充分利用连接条件!
在某种情况下,两个表之间可能不只一个的连接条件,这时在 WHERE 子句中将连接条件完整的写上,有可能大大提高查询速度。
例:
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO
第二句将比第一句执行快得多。
9、能用GROUP BY的就不用DISTINCT
10、能用UNION ALL就不要用UNION!
UNION ALL不执行SELECT DISTINCT函数,这样就会减少很多不必要的资源
三 优化报表就算时间
1、较少冗余单元格
2、慎用隐藏行列
3、慎用合并单元格
4、相同效果的条件属性
同一行(列)中的单元格需要设置相同效果的条件属性时,只需在该行(列)中的某个单元格设置一下条件属性便可,这样能够加快报表的运算速度
5、 相同效果的数据列基本属性设置
报表制作过程中,我们将数据集中的字段拖入单元格时,数据列>基本>数据设置,(分组、列表、汇总)中计算最快的是列表。如制作明细表时有两种设置方法,结果是一样的。
设置方式一:订单ID列数据设置为列表,后面其他跟随其扩展的数据列的数据设置还是采用默认的设置(即分组普通)。
设置方式二:订单ID列数据设置为列表,后面其他跟随其扩展的数据列的数据设置也修改为列表。
上面两种设计方式的数据展示结果是一样的,不过报表的计算速度却不一样;第二种方式的报表计算速度明显会比第一种快。
这是因为数据列设为分组时,报表会将取出的数据进行一次分组聚集的计算,而不管数据有没有重复。因此,对于可实现相同效果的应优先设置其数据列属性为列表。
6、父格设置
FR中设计报表的最重要的思想,就是数据列的扩展,而跟随哪一个单元格扩展是必须要注意的问题。所以父格的设置是很重要的,父格设置不恰当会影响报表的计算速度,甚至导致结果错误。另外,父子格层次树高度,对报表性能也有影响,所以尽量减少父子格层次树高度。如对下图模板:
设置方式一:是否已付父格为运货费,运货费父格为运货商,运货商父格为到货日期,到货日期父格为订购日期,订购日期父格为客户ID,客户ID父
格为订单ID,父子格层次树为6层。
设置方式二:是否已付、运货费、运货商、到货日期、订购日期、客户ID的父格都为订单ID,父子格层次为1层。
上面两种设计方式的数据展示结果是一样的,不过报表的计算速度却不一样;第二种方式的报表计算速度明显会比第一种快。
7、 取消自动调整行高或列宽
在右侧列表中选择单元格属性表-其他属性,设置为不自动调整
8、优化过滤条件
查看模板中是否重复设置了过滤条件,删除多余的过滤条件,即若数据列来自相同的数据集,只需在最左父格设置过滤条件并勾选将父格子作为过滤条件(默认是勾选的)即可。
四 动态参数注入
原理:动态参数注入,是通过注入的值对数据库中的数据进行操作的,只是取对应的数据,所以会提高报表的性能。
注:动态参数注入功能适用于大数据量报表,能够明显的体现出速度优势
五 相邻连续分组
相邻连续分组的操作,是将数据集中连在一起的相同数据才进行合并的。尤其是,在配合SQL中已排好序的列,对其实现的分组时,使用此分组方式其性能比普通分组快些。如下简单示例,查看其报表执行数据信息,可看出其分组方式比普通分组方式更能提高其报表的性能。
六 数据集缓存与共享
1、描述
对于大数据量报表,若每次直接从数据库中查询数据,不仅增加数据库服务器的压力,也极大的影响了取数的速度从而降低了报表的执行速度,为此FineReport提供了数据集缓存与共享功能。
FineReport可先将其数据集查询的结果缓存下来,对于缓存下来的数据,再次使用到相同数据集时,无需再次连接数据库重新查询数据,直接使用缓存下来的结果,即使用FineReport的数据集共享机制,达到资源复用,减少取数时间从而提高了报表的展现速度。
2、数据集缓存
缓存分为缓存至内存和缓存至磁盘。
3、数据集共享属性设置
浏览器中输入http://localhost:8075/webroot/decision,进入FR平台,选择管理系统>系统管理>缓存
七 启用行式引擎执行层式报表
适用场景描述
这样一种报表:报表形式非常简单,只是简单的单数据源明细报表,但是数据量非常大,百万、千万甚至更多。报表取数及计算时间相当长。
希望能够提高报表展示速度,对于用户来说,查询报表不会有滞后的感觉。
思路
1、解决方案
对于单数据源明细报表,可以启用行引擎来执行报表,提高报表展示速度,优化用户体验。
2、原理
普通报表:取出全部数据后再执行报表,最后返回整体的报表结果给浏览器,用户访问到看到结果的时间=报表取数时间+报表执行时间;
行引擎报表:边取数边执行报表,执行到哪页用户就可以看到哪页,用户访问到看到结果的时间=首页数据读取时间+首页计算时间。
2、注意事项
行引擎报表注重的是性能,由原理我们可以想象,对于行引擎报表,每页的计算必须是独立的,即报表不能有单元格关联的复杂运算,类似,为了提高行引擎报表的性能,FR舍弃了一些复杂的功能:
行引擎报表只适用于单数据源、即单元格过滤不能使用,且必须是简单明细表,同时也不支持计算、条件属性等复杂报表功能。
实现步骤
1、模板引擎设置
选择模板>报表引擎属性,勾选用行式的引擎来执行报表,再勾选下面的使用按页运算分段执行报表,每页记录数使用默认值30,如下图:
行式引擎按页取数只适用于Oracle,mysql,hsql和sqlserver2012及以上数据库,其他数据库,
如sqlite,access,sqlserver2005等必须手动编写分页sql,才能实现按页取数,对于需要编写分页sql的数据库,请参考单数据集分页SQL实现层式报表章节。
注:sqlserver2012及以上是支持行式引擎的的,但是sql中一定要写order by。
行式引擎只适用于单数据源报表,对于多数据集报表,行式引擎无法实现对其进行分页显示,多数据源的层式报表的实现请查看多数据集实现层式报表。