摘 要: 為減少數(shù)據(jù)冗余,根據(jù)規(guī)范化理論設(shè)計的數(shù)據(jù)庫不能直接輸出分類匯總的結(jié)果,需要進行轉(zhuǎn)換生成交叉表,并利用水晶報表的Push模式實現(xiàn)對交叉表的顯示。在.NET和SQL Server環(huán)境下,探討了動態(tài)交叉表的生成和利用水晶報表動態(tài)綁定數(shù)據(jù)源顯示數(shù)據(jù)的方法,并給出了較為詳細的實現(xiàn)過程。
關(guān)鍵詞: 動態(tài)交叉表;水晶報表;Push模式;動態(tài)綁定
0 引言
信息系統(tǒng)中都不能缺少報表,而交叉表是種常見的報表形式,它將源表中的數(shù)據(jù)分組匯總后,一組列在表的左側(cè),另一組列在表的上部,從而形成一種分類匯總表格[1]。動態(tài)交叉表是按用戶呈現(xiàn)數(shù)據(jù)的要求,不僅對數(shù)據(jù)進行分類,同時還要根據(jù)表中數(shù)據(jù)的情況動態(tài)創(chuàng)建列,把數(shù)據(jù)行信息置換到表格列處并進行匯總。
水晶報表是一款商務(wù)智能軟件,主要用于設(shè)計產(chǎn)生報表,是業(yè)內(nèi)功能最強的報表系統(tǒng)[2],其出現(xiàn)的目的就是使計算機參與到辦公系統(tǒng)業(yè)務(wù)流程中。
1 問題的提出
以我校學(xué)生成績管理系統(tǒng)為例,為消除存儲異常,減少數(shù)據(jù)冗余,保證數(shù)據(jù)的完整性,按規(guī)范化設(shè)計理論設(shè)計的數(shù)據(jù)庫含多張表,其中與成績有關(guān)的3個表的關(guān)系模式學(xué)生信息表S(學(xué)號、姓名、性別、班號)、課程信息表C(課號、課名)、學(xué)生成績表SC(學(xué)號、課號、學(xué)期、成績)之間的關(guān)系圖如圖1所示。學(xué)生成績表中的數(shù)據(jù)形式如圖2所示。實際工作中需要打印的學(xué)生成績表如圖3所示。
由此可見,數(shù)據(jù)庫中存儲的數(shù)據(jù),在某些應(yīng)用中,需要生成動態(tài)交叉表,因不同班級不同學(xué)期學(xué)生學(xué)習(xí)的課程不一樣,生成的動態(tài)交叉表的列項的個數(shù)和名稱都是不固定的。經(jīng)驗表明,水晶報表雖然功能強大且使用方便,但它要求設(shè)計表格時所使用的表名以及列名與使用時必須一致。本文研究了水晶報表的Pull模式和Push模式[3],提出了在數(shù)據(jù)庫端生成一個表格列項固定的動態(tài)交叉表,利用Push模式動態(tài)綁定數(shù)據(jù)源把數(shù)據(jù)推送給水晶報表引擎的方法。
2 生成動態(tài)交叉表
首先在數(shù)據(jù)庫服務(wù)器端完成列項名稱與個數(shù)固定的動態(tài)交叉表的生成。設(shè)每學(xué)期最多有8門課,實現(xiàn)方法如下:
(1)創(chuàng)建函數(shù)ufGetCourse,功能是篩選出某班對應(yīng)學(xué)期的課程,并按順序編號。運行結(jié)果如圖4所示。
create function ufGetCourse(@bno int,@tnum int)returns table
as return
?。?/p>
select col=count(*),tc1.cno,cname
from(select distinct cno from s join sc on s.sno=sc.sno where bno=@bno and tnum=@tnum)as tc1
join(select distinct cno from s join sc on s.sno=sc.sno where bno=@bno and tnum=@tnum)as tc2
on tc1.cno>=tc2.cno
join c on tc1.cno=c.cno
group by tc1.cno,cname
?。?/p>
?。?)創(chuàng)建存儲過程upCreateJCB,功能是按學(xué)號對應(yīng),把順序編號為1的4號課程成績放在Cj1列中,把順序編號為2的5號課程成績放在Cj2列中,以此類推,最后計算每個學(xué)生的總評分。運行結(jié)果如圖5所示。
create proc upCreateJCB
@bno int,@tnum int
as
declare @str varchar(100),@cstu varchar(100),@cno int,@sno char(10),@sname varchar(50),@sc int,@col int,@ZF int
create table #t
?。?/p>
sno char(10),sname varchar(50),ZF decimal(5,1),
cj1 int,cj2 int,cj3 int,cj4 int,cj5 int,cj6 int,cj7 int,cj8 int
?。?/p>
declare my_C cursor for select s.sno,sname,col,sc.cno,Score from S join SC on s.sno=sc.sno join(select*from dbo.ufGetCourse(@bno,@tnum))as c on sc.cno=c.cno where bno=@bno and tnum=@tnum order by sno
open my_C
fetch next from my_C into@sno,@sname,@col,@cno,@sc
while @@fetch_status=0
begin
select@cstu=@sno,@ZF=0
insert into#t(sno,sname)values(@sno,@sname)
while@@fetch_status=0 and@cstu=@sno
begin
set@str=′update#t set cj′+cast(@col as varchar(2))+′=′′′+cast(@sc as varchar(3))+′′′′
set@str=@str+′where sno=′+@sno
exec(@str)
set@ZF=@ZF+@sc
fetch next from my_C into@sno,@sname,@col,@cno,@sc
end
--更新總評分
update #t set ZF=@ZF where sno=@cstu
end
select*from#t
drop table#t
close my_C
deallocate my_C
3 水晶報表動態(tài)綁定數(shù)據(jù)源
應(yīng)用程序的前臺界面在.NET平臺下進行水晶報表設(shè)計,采用Push模式在程序中動態(tài)加載數(shù)據(jù)源和報表,用動態(tài)傳參方式把表頭的cj1~cj8更換成對應(yīng)的中文課程名,方法如下:
(1)建立解決方案。在解決方案資源管理器中添加“Crystal報表”模板。方法為:添加→新建項→Crystal報表→命名報表為MyCry.rpt→作為空白報表。
?。?)在字段資源管理器中通過“報表專家”完成報表設(shè)計。方法為:數(shù)據(jù)庫字段→數(shù)據(jù)庫專家→創(chuàng)建新鏈接→OLE DB(ADO)→Microsoft OLE DB Provider for SQL Server→填寫鏈接數(shù)據(jù)庫的信息→選擇對應(yīng)的數(shù)據(jù)庫→選擇存儲過程upCreateJCB→把涉及的字段拖拽到水晶報表細節(jié)欄中并填上表格線。
(3)在字段資源管理器中添加參數(shù)字段:班級名classname、學(xué)期TermNo、Cj1~Cj8,并把這些參數(shù)字段拖拽到水晶報表的頁眉欄處。
經(jīng)過上述過程設(shè)計的報表MyCry.rpt如圖6所示。
?。?)在窗體上放一個CrystalReportViewer控件并命名為crv,用C#編程動態(tài)加載數(shù)據(jù)源和報表。代碼如下:
//程序開始處需對兩個名字空間進行引用
using CrystalDecisions.Shared;
using CrystalDecisions.CrystalReports.Engine;
//Load事件下執(zhí)行的代碼
int num,bj=197,xq=1;//197為班級號,1為第1學(xué)期
SqlConnection con=new SqlConnection();
con.ConnectionString="Data Source=.;Initial Catalog=Student_Score;Integrated Security=True";
con.Open();
string strSql="exec upCreateJCB"+bj.ToString()+","+xq.ToString();
SqlDataAdapter da=new SqlDataAdapter(strSql,con);
DataTable d1=new DataTable();
da.Fill(d1);
MyCry ocr=new MyCry();
ocr.Load("MyCry");
ocr.SetDataSource(d1);
//向水晶報表中傳參數(shù)
ParameterFields_ps=new ParameterFields();
//TermNo參數(shù)
ParameterField_p=new ParameterField();
ParameterDiscreteValue_v=new ParameterDiscreteValue();
_p.ParameterFieldName="TermNo";
_v.Value=xq;
_p.CurrentValues.Add(_v);
_ps.Add(_p);
//ClassName參數(shù)
_p=new ParameterField();
_v=new ParameterDiscreteValue();
_p.ParameterFieldName="ClassName";
_v.Value=bj;//此處可先從班級表中讀取班級名然后傳遞班級名,本例略
_p.CurrentValues.Add(_v);
_ps.Add(_p);
//n個課程名稱參數(shù)
strSql="select*from dbo.ufGetCourse("+bj.ToString()+","+xq.ToString()+")";
SqlDataAdapter db=new SqlDataAdapter(strSql,con);
DataTable d2=new DataTable();
db.Fill(d2);
for(num=1;num<=d2.Rows.Count;num++)
{
_p=new ParameterField();
_v=new ParameterDiscreteValue();
_p.ParameterFieldName="cj"+num.ToString();
_v.Value=d2.Rows[num-1][2].ToString();
_p.CurrentValues.Add(_v);
_ps.Add(_p);
}
//報表中共有8列,不足8列時后面內(nèi)容填空
for(;num<=8;num++)
{
_p=new ParameterField();
_v=new ParameterDiscreteValue();
_p.ParameterFieldName="cj"+num.ToString();
_v.Value="";
_p.CurrentValues.Add(_v);
_ps.Add(_p);
}
crv.ParameterFieldInfo=_ps;
crv.ReportSource=ocr;
4 結(jié)論
本文提出了利用水晶報表顯示并打印動態(tài)交叉表的一種方法,圖3就是本校學(xué)籍管理系統(tǒng)中用本方法打印的成績匯總表,由于來源于真實數(shù)據(jù),故姓名處進行了處理。
參考文獻
[1] 張賢斌,費樹岷.管理信息系統(tǒng)中動態(tài)交叉表的實現(xiàn)方式研究[J].計算機應(yīng)用工程技術(shù),2008,4(4):995-996.
[2] 叢鳳俠,楊玉強.通用水晶報表自動生成技術(shù)研究[J].計算機技術(shù)與發(fā)展,2013,23(5):54-57.
[3] 錢哨,李揮劍,李繼哲,等.C#WinForm實踐開發(fā)教程[M].北京:中國水利水電出版社,2010.