交叉报表是一种常见的报表类型,而且开发起来也是比较烦琐的一种报表,在
ActiveReport
中,对交叉报表提供了足够的灵活性,使你能够应对各种复杂的业务逻辑。在上篇随笔演示了显示主从表后,本篇随笔简单介绍如何制作交叉报表。
交叉报表的一个常见应用就是用作显示销售额的报表上,例如,显示多个连锁店一年内每个月的销售额,常把月份作为列来显示,每个店用一行来表示
:
店名
1
月
2
月
3
月
4
月
……………
AC 500 200 10000 50000
……………
BC 511 85245 4545 124578
……………
但是在数据库中的存储常常采用下面的方式
Sales Month Shop
12312 1 AB
243423 2 AB
323232 3 AB
1231312 1 BC
1232 2 BC
这样就需要在显示前对数据进行处理,把销售额和月份转换到列上,我们可以在取数据时使用
sql
来进行这些操作,在这里,为了演示在
activeReport
中的使用,把转换放到报表里来作,为了简化例子,我们只显示第一个季度每月的销售额。
1.
取数据:
使用
Select Sales,Month,Shop from CrossReport Order by Shop
这样的
sql
直接取数据,不作任何合计或转换处理。
2.
转换:
我们来定义一个简单的对象来表示最终要显示的记录
:
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
写在代码中,调试成问题,如果写成存储过程,有破坏了封装。所以相比之下,在代码中进行的转换工作虽然较复杂,但是还是具有灵活的优势的。