摘要:交叉表的設計一直是程序開發的難點,充分利用C#2005數據集和數據表的離線功能,我們可以把一張表的數據行變成另一個內存表的數據列,這樣我們就可以非常容易地進行數據匯總統計,統計要求符合中國報表特色。
關鍵詞:離線模式;數據集;數據表;交叉表
中圖法分類號:TP31 文獻標識碼:B 文章編號:1009-3044(2008)15-20ppp-0c
Implement CrossTable in C#2005
PANG Yi-fan
(WuXi High Vocational Education,Wuxi 214028,China)
Abstract:Cross-table design has been developed and difficult process,full use of C# 2005 dataset and datatable's offline function,we can transform datarows of one table into another datacolumns of memory table,so that we can be very easy data summary statistics, the statistical requirements in line with China's statements characteristics.
Key words:OffLine;DataSet;DataTable;CrossTable
1 ADO.Net下的離線模式
我們知道在C#2005中進行數據庫開發的時候,需要利用ADO.Net技術進行開發,在ADO.Net之前,一般開發數據庫程序的時候,都需要在線訪問數據庫服務器中的數據,也就是說客戶端程序在運行的時候,都和遠程的數據庫服務器連接,直到關閉你的運行程序為止。這一種聯機方式在局域網內沒有什么問題,但是在今天以互聯網為基礎的環境下,就存在很大的弊端,主要原因就是客戶連接到數據庫是一種比較耗內存的資源,當有大量用戶訪問數據庫服務器,服務器就會占據大量的內存,并且網絡的傳輸效率會降低。為此ADO.Net提出了離線模式,就是為克服在線模式下的缺點而設計的,解決方法就是當用戶需要數據的時候,連接數據庫服務器,當數據下載到客戶端的時候,馬上斷開與數據庫服務器的連接,這樣就節省了大量的資源。離線模式的技術解決之道用數據集來表示(DataSet),一個數據集可以存放任意數量的數據表(DataTable),而每一個DataTable之間還可以建立關系(TableRelation),有了這樣的概念以后,我們可以認為一個DataSet就是一個數據庫在客戶端的副本。具體模式圖表如下:

2 使用DataSet及DataTable:
DataSet其實就是數據集,已經說過DataSet是把數據庫中的數據映射到內存緩存中所構成的數據容器,對于任何數據源,它都提供一致的關系編程模型。在DataSet中既定義了數據表的約束關系以及數據表之間的關系,還可以對數據表中的數據進行排序等。DataSet使用方法一般有三種:
1.把數據庫中的數據通過DataAdapter(數據適配器)對象填充DataSet。
2.通過DataAdapter對象操作DataSet實現更新數據庫。
3.把XML數據流或文本加載到DataSet。
2.1 建立SQL 2000數據表
下面就來詳細探討以上DataSet和DataTable配合使用方法的具體實現。我使用的利用DataAdapter對象填充DataSet,使用語言是C#,特別注意的是動態建立內存表成績匯總表的建立過程。后臺數據庫是SQL 2000 Server(數據庫文件是Test)。
Test中有三個表(學生表Stu,課程表Course,成績表Score)
學生表內容如下:

課程表內容如下:

成績表內容如下:

從成績表我們可以查出任何一個人的每一門課程的成績,但是如果要統計全班所有課程就比較困難;這時我們必須用到交叉表,但是交叉表在數據庫中沒有定義,編程人員必須利用ADO.Net技術在內存中動態生成如下的表格(成績匯總表):

2.2 用C#2005創建動態表
(1)創建一個過程(CreateTable)來建立需要的表
private void CreateTable()
{Settings st = new Settings();
conn = new SqlConnection(st.Setting);//建立數據庫連接
conn.Open();
dap = new SqlDataAdapter(\"select sno,sname from stu\", conn);
dt = new DataTable(\"學生表\");
ds = new DataSet();
dap.Fill(ds, \"學生表\");//內存中建立學生表,具有字段學號和姓名
dap = new SqlDataAdapter(\"select coursename from courseName \", conn);
dt = new DataTable(\"課程名稱\");
dap.Fill(ds, \"課程名稱\");//建立課程表,只有字段課程名稱
dt = new DataTable(\"分數表\");
ds.Tables.Add(\"分數表\");
//以下是動態的建立成績匯總表,而不是用適配器來自動建立內存表,這時建立交叉表的關鍵
DataTabledt1 = new DataTable(\"成績匯總表\");
ds.Tables.Add(\"成績匯總表\");
ds.Tables[\"成績匯總表\"].Columns.Add(\"sno\", typeof(System.String));
ds.Tables[\"成績匯總表\"].Columns[\"sno\"].MaxLength = 6;
ds.Tables[\"成績匯總表\"].Columns.Add(\"sname\", typeof(System.String));
ds.Tables[\"成績匯總表\"].Columns[\"sname\"].MaxLength = 10;
string Express = string.Empty;
int count = ds.Tables[\"課程名稱\"].Rows.Count;
foreach (DataRow dr in ds.Tables[\"課程名稱\"].Rows)
{
ds.Tables[\"成績匯總表\"].Columns.Add((string)dr[\"coursename\"], typeof(System.Double));
ds.Tables[\"成績匯總表\"].Columns[(string)dr[\"coursename\"]].DefaultValue = 0;
Express += (string)dr[\"coursename\"] + \"+\";
}
Express = Express.Substring(0, Express.Length - 1);
//建立計算字段
ds.Tables[\"成績匯總表\"].Columns.Add(\"總分\", typeof(System.Double));
ds.Tables[\"成績匯總表\"].Columns[\"總分\"].Expression = Express;
ds.Tables[\"成績匯總表\"].Columns.Add(\"平均分\", typeof(System.Double));
ds.Tables[\"成績匯總表\"].Columns[\"平均分\"].Expression = \"總分/\" + count.ToString();
DataColumn[] dc = new DataColumn[1];
dc[0] = ds.Tables[\"成績匯總表\"].Columns[\"sno\"];
ds.Tables[\"成績匯總表\"].PrimaryKey = dc;//建立主關鍵字
//加數據
foreach (DataRow dr in ds.Tables[\"課程名稱\"].Rows)
{
//3表中找出每門課程對應的所有信息學號,姓名,課程號,課程名,分數
SqlDataAdapter dapFen = new SqlDataAdapter(\"select a.sno,a.sname,b.coursename,c.courseid,c.fen from stu a,course b,Score c where a.sno=c.sno and b.courseid=c.courseid and b.coursename='\" + dr[\"coursename\"].ToString() + \"'\", conn);
ds.Tables[\"分數表\"].Rows.Clear();
dapFen.Fill(ds, \"分數表\");//建立分數表,(數學,物理,化學,語文)
foreach (DataRow drr in ds.Tables[\"分數表\"].Rows)
{
DataRow drfind = ds.Tables[\"成績匯總表\"].Rows.Find(drr[\"sno\"]);
if (drfind == 1)//匯總表中沒有相應的課程記錄就添加
{
DataRow drd = ds.Tables[\"成績匯總表\"].NewRow();
drd[\"sno\"] = drr[\"sno\"];
drd[\"sname\"] = drr[\"sname\"];
drd[(string)dr[\"coursename\"]] = drr[\"fen\"];//課程名對應相應的成績
ds.Tables[\"成績匯總表\"].Rows.Add(drd);
}
else//有記錄就修改其分數
{
//找到的記錄所在的行位置
int i = ds.Tables[\"成績匯總表\"].Rows.IndexOf(drfind);ds.Tables[\"成績匯總表\"].Rows[i][(string)dr[\"coursename\"]] = drr[\"fen\"];
}
}
}
conn.Close();//斷開連接,進入離線模式
}
(2)建立過程CreteField來建立DataGridView的列和內存表(成績匯總表的綁定關系)
private void CreateField()
{
Grid.Columns.Add(\"c1\", \"學號\");
Grid.Columns[\"c1\"].DataPropertyName = \"sno\"; //數據綁定
Grid.Columns[\"c1\"].Width = 75;
Grid.Columns[\"c1\"].Frozen = true;//凍結列
Grid.Columns.Add(\"c2\", \"姓名\");
Grid.Columns[\"c2\"].DataPropertyName = \"sname\";
Grid.Columns[\"c2\"].Width = 65;
Grid.Columns[\"c2\"].Frozen = true;
int i = 3;
foreach (DataRow dr in ds.Tables[\"課程名稱\"].Rows)
{
Grid.Columns.Add(\"c\" + i.ToString(), (string)dr[\"coursename\"]);
Grid.Columns[\"c\" + i.ToString()].DataPropertyName = (string)dr[\"coursename\"];
Grid.Columns[\"c\" + i.ToString()].Width = 80;
i++;
}
Grid.Columns.Add(\"總分\", \"總分\");
Grid.Columns[\"總分\"].DataPropertyName = \"總分\";
Grid.Columns[\"總分\"].Width = 80;
Grid.Columns.Add(\"平均分\", \"平均分\");
Grid.Columns[\"平均分\"].DataPropertyName = \"平均分\";
Grid.Columns[\"平均分\"].Width = 80;
Grid.DataSource = ds.Tables[\"成績匯總表\"];
(3)我們只需要在窗體的OnLoad事件中調用上面定義的兩個過程即可,具體代碼如下:
public partial class FrmHZ : Form
{ SqlConnection conn;
SqlDataAdapter dap;
DataSet ds;
DataTable dt;
string SQLStr = string.Empty;
//以上定義的窗體級成員
public FrmHZ()
{
InitializeComponent();
}
private void FrmHZ_Load(object sender, EventArgs e)
{
CreateTable();
CreateField();
}
}
3 結束語
交叉表在數據統計中占有比較重要的地位,但是數據表中并不存在交叉表,所以需要我們根據現成的表來動態建立內存中的交叉表。利用DataTable對象中Columns集合來動態建立交叉表的數據列,而該數據列來自數據表Course的行內容。(當我們在課程表中加上一門新的課程的時候,交叉表中的內容會自動變化)
參考文獻:
[1]黃忠成.Framework的設計與應用[M].北京:電子工業出版社,2006年1月,90-165.
[2]ADO.Net高級編程[M](美)Glenn Johnson 北京:清華大學出版社,2006年6月50-198.
[3]代方震.Visual C# 2005程序設計.北京:人民郵電出版社,2007年9月,206-243.
收稿日期:2008-2-18
作者簡介:龐一凡(1965-),江蘇無錫人,無錫機電高等職業技術學校信息系教師,軟件工程師,研究方向:計算機軟件開發(C#.Net,Delphi 和SQL Server下的網絡數據庫程序開發)。