【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 |
這樣就需要在顯示前對資料進行處理,把銷售額和月份轉換到列上,我們可以在取資料時使用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寫在代碼中,調試成問題,如果寫成預存程序,有破壞了封裝。所以相比之下,在代碼中進行的轉換工作雖然較複雜,但是還是具有靈活的優勢的。