【IT168 技术文档】
交叉报表是一种常见的报表类型,而且开发起来也是比较烦琐的一种报表,在ActiveReport中,对交叉报表提供了足够的灵活性,使你能够应对各种复杂的业务逻辑。在上篇随笔演示了显示主从表后,本篇随笔简单介绍如何制作交叉报表。
交叉报表的一个常见应用就是用作显示销售额的报表上,例如,显示多个连锁店一年内每个月的销售额,常把月份作为列来显示,每个店用一行来表示:
店名 | 1月 | 2月 | 3月 | …………… |
AC | 500 | 200 | 10000 | …………… |
BC | 511 | 85245 | 4545 | …………… |
Sales | Month | Shop |
12312 | 1 | AB |
243423 | 2 | AB |
323232 | 3 | AB |
1231312 | 1 | BC |
1232 | 2 | BC |
public class Sale { public decimal money1; // 一月 public decimal money2; // 二月 public decimal money3; // 三月 public string shopname; }
同时在定义一个Sale的集合sales,来保存转换后的数据。
由于在表中每个店会对应多条记录,为了把多条记录合并为一条,要进行下面的转换动作:
// 用来保存已经计算过的店铺,保证每个店铺只有一条记录 ArrayList shopname = new ArrayList(); while (dr.Read()) { if ( ! shopname.Contains(dr.GetString( 2 ))) // 该店铺的第一条记录 { Sale s = new Sale (); s.shopname = dr.GetString( 2 ); // 取店名 shopname.Add(s.shopname); if (dr.GetInt32( 1 ) == 1 ) // 一月 { s.money1 = dr.GetDecimal( 0 ); } else if (dr.GetInt32( 1 ) == 2 ) // 二月 { s.money2 = dr.GetDecimal( 0 ); } else if (dr.GetInt32( 1 ) == 3 ) // 三月 { s.money3 = dr.GetDecimal( 0 ); } sales.Add(s); } else // 不是该店铺的第一条记录 { Sale s = (Sale)sale [sales.Count - 1 ]; if (dr.GetInt32( 1 ) == 1 ) { s.money1 = dr.GetDecimal( 0 ); } else if (dr.GetInt32( 1 ) == 2 ) { s.money2 = dr.GetDecimal( 0 ); } else if (dr.GetInt32( 1 ) == 3 ) { s.money3 = dr.GetDecimal( 0 ); } } }
3. 表示:
上面是对从数据库中取出的记录作转换,将其变成在报表上要显示的格式。接下来就要在报表上显示Sales集合中的数据了。我们可以按照前几篇随笔中介绍的方法来作:
在界面上摆放控件,并设置其FiledName字段
在报表的DataInitialize事件中设置Filed集合,取出数据:
this .Fields.Add( " money1 " ); this .Fields.Add( " money2 " ); this .Fields.Add( " money3 " ); this .Fields.Add( " shopname " ); this .GetReportData(); // 取数据并作转换
设置一个标记来表示是否显示到了最后一条记录:
int index = 0 ;
在FetchData事件中显示Sales集合中的数据:
if (index == sales.Count ) // 如果到了最后一条记录,就跳出 { eArgs.EOF = true ; return ; } else { eArgs.EOF = false ; } Sale s = (Sale)sales[index]; this .Fields[ " shopname " ].Value = s.shopname; this .Fields[ " money1 " ].Value = s.money1; this .Fields[ " money2 " ].Value = s.money2; this .Fields[ " money3 " ].Value = s.money3; index += 1 ;
按照上面的步骤,主要的代码都完成了,当然要在窗体上显示,还要加一个Viewer,然后指定加载的报表:
ActiveReports1 rpt = new ActiveReports1(); rpt.Run(); this .viewer1.Document = rpt.Document;
如果你不满意显示的效果,可以给报表加上线框,让其显示成表格。
总结:
例子中的代码有重复,但是为了说明转换的过程,没有作优化,另外,也可以看到,代码中使用了ArrayList,出现了装箱,拆箱的动作,所以性能还有优化的空间。
从表数据到显示用数据的转换可以在Sql中作,但是业务逻辑较复杂的时候,Sql就显得力不从心,例如,显示每月的数据,而且还有收入,支出,如果再加上税收,折扣,损耗,租金,和上年同期的比较等等无法预测的业务逻辑,如果把SQL写在代码中,调试成问题,如果写成存储过程,有破坏了封装。所以相比之下,在代码中进行的转换工作虽然较复杂,但是还是具有灵活的优势的。