top
Loading...
用ADO.NET管理數據庫

使用ADO.NET管理數據庫中的關系(relation)非常簡單。作為返回單個行集合(rowset)到數據存儲(data store)的代替,你可以返回多個行集合并在數據集(DataSet)中將它們關聯起來。在ADO.NET中使用關聯的數據表(DataTable)有很多好處,包括有將數據存儲為層次結構的能力、更容易更新數據、有在列中使用表達式的能力等等。

本文介紹ADO.NET中基于列(column-based)的表達式和計算。我將演示數據列(DataColumn)上集合函數的使用、匯總、在整個數據集中執行其它類型的計算以及在數據表之間連接數據列。文中還舉了幾個實例。

在SQL查詢中合計(summing)和平均值(averaging)對你來說很老套了,這要感謝ANSI SQL中有類似SUM 和AVG的集合函數。SQL允許列的計算,例如按次序把產品單價乘以數量產生擴充價格。現在ADO.NET提供了一條途徑擴展這些數據源之外的特征并進入n層結構應用程序中間或上層。在ADO.NET中使用列表達式,你能用數據集中的計算值建立自己的列,在同一行中計算其它列的值,甚至通過數據關系(DataRelation)從父數據表或子數據表獲取值。使用ADO.NET中的基于列的表達式和計算形成了新的管理數據的技術。

當然,在ADO.NET中使用基于表達式的列、集合和計算有利也有弊。表達式可用于數據集的單個數據表或者通過數據關系交叉的兩個數據表對象。本文將解釋在ADO.NET和SQL中基于表達式的列有什么不同,以及你能從它們中得到什么東西。本文討論了許多操作,包括用表達式上滾(roll up)和下滾(roll down),它依賴于DataRelation對象的關系。我將演示怎樣在數據列對象中建立表達式,怎樣使用數據集和SQL建立集合函數,怎樣在數據集中上滾和下滾字段,怎樣在數據集中執行列的計算。

SQL中的表達式

SQL表達式為達到不同的目的有多種格式,包括字符串格式、用戶定義函數和數學計算。如果SQL語句將姓和名連接到一起、按訂單條目次序計算擴充價格、或者在SQL Server 2000中包含一個用戶定義函數來檢索一個訂單日期,SQL語句就包含了表達式。

表達式為程序開發人員從數據庫中的其它字段衍生出的行集合中的返回值提供了很大的靈活性。關系型和標準的數據庫不會保存訂單條目的擴充價格,它只存儲單價和數量。擴充價格能從這些值中計算出來,這樣避免了數據不同步。例如,如果在一行中存儲了數量、單價和擴充價格,可能出現數量為100,單價為7,擴充價格為100的情況。這種情況不該發生,但是如果存儲了冗余數據就可能發生了。事務性數據庫的通用準則是不存儲可以衍生出的信息,例如擴充價格。

這樣就有了SQL表達式表演的舞臺了。擴充價格能通過在SQL語句中建立計算列,將單價乘以數量衍生出。擴充價格又能計算帳戶的折扣率等信息。下面的SQL代碼演示了怎樣在SQL表達式中連接字符串。例子將姓與名連接在一起并返回大寫的全名,名字位于前面:

USE pubsGOSELECT au_fname AS FirstName,       au_lname AS LastName,       au_lname + ', ' + au_fname AS FullName1,       (UPPER(au_fname) + ' ' + UPPER(au_lname)) AS FullName2FROM    authorsORDER BY       au_lname,       au_fname

SQL表達式能夠在行集合中格式化字符串并執行計算,但是為了達到這個目的,你要注意一些問題:如果用上面的SQL語句填充數據集,并且首行的數量列被修改了,表達式列不會跟著改變。例如,如果數量為10,價格為7,現在數量變為5,ExtendedPrice列的值仍為70(10×7),數據沒有同步。出現這種情況的主要原因是表達式沒有從SQL語句傳遞到ADO.NET數據集。

數據列表達式

表達式也可以通過ADO.NET數據列對象定義。作為通過SQL語句計算擴充價格的替代者,數據列可以被定義用于表現擴充價格。在SQL語句和數據列中使用表達式的區別是,如果表達式中的某個字段被修改了,數據列會自動更新表達式所定義的字段,但SQL表達式不會更新數據列。

下面的代碼演示了怎樣使用SQL語句填充數據集的數據表并建立一個新數據列描述計算表達式,該表達式使用數據集的唯一數據表中的其它字段得到。接著該數據表的默認視圖被綁定到ASP.NET的一個叫做grdOrderDetail 的DataGrid控件。

這段代碼使用訂單詳細信息建立和填充了一個數據集。接著一個列被添加到該數據集的數據表,用于表現該表達式。該列叫ExtendedPrice,數據類型為浮點型,它的表達式是產品單價和數量列。該表達式能涉及數據表的任意數據列,從當前的數據行中取得值。例如,如果第一行數量為10,單價為7,擴充價格列將使用的值為70。

在計算中表達式可以包括從數據表中其它字段得到的字面值。例如,定義擴充價格的表達式可以稍作改變,使它考慮折扣率:

oDs.Tables["OrderDetail"].Columns.Add("ExtendedPrice", typeof(decimal), "(UnitPrice * Quantity) * (1 - Discount)");

試著改變UnitPrice、Discount或Quantity數據列的值,結果是與從SQL表達式衍生的列不同,ExtendedPrice數據列也被更新了。該特征對應用程序很重要,特別是在用戶能修改購物車,確認改變,然后查看更新后的總價格的情況下。

表達式也可以用于表現其它數據類型,例如邏輯型和字符型值。下面的代碼演示了添加一個字段用于顯示一個作者是否有折扣:

oDs.Tables["OrderDetail"].Columns.Add("GetsDiscount", typeof(bool),"Discount > 0");

你能使用AND、OR或NOT條件建立混合表達式來考慮多個條件,這樣可以加強前面的表達式。還有一些操作符,包括LIKE和IN也可以在表達式中使用。

表達式也能表現字符串值,例如從數據表中取得姓和名并連接到一起。下面的代碼段將ProductName與ProductID連接在一起:

oDs.Tables["OrderDetail"].Columns.Add("stringfield", typeof(string),"ProductID + '-' + ProductName");

函數

如果你希望列包含有更復雜邏輯的表達式,可以在列中嵌入一些函數。表達式能包含Len、Iif、 IsNull、Convert、Trim和Substring等函數。這些函數為建立表達式提供了更大的靈活性。Len函數計算字符串的長度:

oDs.Tables["OrderDetail"].Columns.Add("LengthOfProductName",typeof(int), "Len(ProductName)");

Iif函數是一個迭代的If語句,象Visual Basic .NET中的IIf一樣。它有三個參數并計算第一個參數的真假。如果第一個參數計算值為真,將從Iif函數返回第二個參數,否則返回第三個參數。下面基本上是一個濃縮的If...Then...Else語句,能簡單地寫成嵌入表達式:

oDs.Tables["OrderDetail"].Columns.Add("Inventory", typeof(string),"Iif(Quantity < 10,'A few left', 'Plenty in stock')");

IsNull函數計算第一個參數,看它是否與System.DbNull相等。如果計算結果為假,函數返回第一個參數值,如果為真則返回第二個參數值。這在不允許空值,并且希望用空字符串或占位符代替時使用:

oDs.Tables["OrderDetail"].Columns.Add("DiscountString",typeof(string), "IsNull(Discount, '[null value]')");

Trim函數刪除字符串值尾部的空格。Convert函數將表達式中的數據類型轉換為函數第二個參數所指的數據類型。Substring函數返回字符串的一部分,可用于將長字符串剪短,只顯示定長的字符串,它可以返回字符串的任意部分并根據需要與其它的函數組合使用:

oDs.Tables["OrderDetail"].Columns.Add("ShortProduct", typeof(string), "Substring(ProductName, 1, 10)");

集合和關系

表達式中的嵌入集合函數能幫助你建立一個表現更復雜邏輯的表達式。如果要建立一個列計算跨多個行的值怎么辦?最好加入集合函數。

假定在一個數據集中有類似SQL Server的Northwind數據庫中的訂單到訂單細節的關系結構,那么用包含集合函數的表達式建立列就很直接。下面的代碼演示了怎樣建立一個包含結構的數據集,在該數據集中訂單位于父數據表而訂單細節位于子數據表。這些數據表對象通過一個叫Orders2OrderDetails的數據關系彼此關聯。

請注意代碼是怎樣建立表達式列并添加到Order 數據表的。第一個表達式建立一個表現每個訂單的詳細信息匯總的列。特別的是OrderTotal數據列有一個表達式用于匯總OrderDetail數據表的基于表達式的ExtendedPrice數據列。你能發現,可以跨越數據關系使用集合函數并將它用在另一個基于表達式的列上。

ADO.NET中還有其它的集合函數,包括Sum、Avg、Max、Min、StDev、Var和Count。下面的代碼演示了怎樣使用Avg函數得到訂單詳細數量的平均值。關鍵在于使用父(parent)和子(child)關鍵字通過數據關系連接數據:

oDs.Tables["Order"].Columns.Add("AvgQuantity", typeof(decimal),  Avg(Child(Order2OrderDetail).Quantity)");

Child函數接受數據關系名來獲取子行集合。該參數是可選的,只有在源數據表中的子關系多于一個時才需要。因此,如果數據表只有一個子表,語法可以簡化:

oDs.Tables["Order"].Columns.Add("AvgQuantity", typeof(decimal), Avg(Child.Quantity)");

向下滾動和計算

Parent函數與Child函數工作類似,只是它沿關系鏈上行到達父數據表。在ADO.NET代碼中這兩個函數幫助建立一個假的GROUP BY函數性。

這些關鍵字的另一個操作是上滾或下滾從一個數據表到另一個數據表不變的值。經常有人問我怎樣將一個父數據表和子數據表連接成一個數據表,這樣才能在一個DataGrid控件中顯示。使用父關鍵字可以將字段下滾到子數據表,接著只將該子數據表綁定到DataGrid控件。例如,需要顯示OrderDetail數據表中每一行的訂單日期,可以添加一個使用父關鍵字的數據列:

oDs.Tables["OrderDetail"].Columns.Add("OrderDate", typeof(string), "Parent.OrderDate");

這個特征使得不需要作任何計算就可以上滾和下滾字段。使用Child關鍵字可以從父表中下滾字段并綁定到DataGrid控件。執行該操作后你得到了一個兩位小數的行集合,與從SQL語句中得到的一樣。要記住,如果打算使用單個行集合中的數據,最好返回單個行集合到數據表。但是,如果你想在數據集中使用關系結構,Parent關鍵字給了你顯示數據的靈活性。

另一個值得一看的特征是數據表的Compute函數,該函數使用給定了過慮條件的當前數據表上的集合函數來計算。例如,能給數據表加一列用于計算訂單總價大于999的訂單總數量。

在下例所示的Compute函數的第一個參數中,執行了一個集合函數計算符合條件的所有OrderTotal值:

//顯示訂單價格大于999的訂單數量int iCnt = (int)oDs.Tables["Order"].Compute("Count(OrderTotal)",    "OrderTotal >= 1000");lblTest.Text = iCnt.ToString() + " orders are at least $1000";

在第二個參數中指定了過濾器,將集合函數限定為只包含符合條件的行。代碼計算OrderTotal大于或等于1000的行。這是一個在數據表中快速執行計算的強大工具,尤其是能使用過慮。例如,你能很容易地查找出訂X貨物的顧客數量和訂Y貨物的顧客數量,而不需要循環查詢數據庫。

注意綁定到表達式的數據列對象不能手動更新。綁定到表達式的列不會被覆蓋,除非表達式被刪除。當然,這些數據也不會與數據庫或XML文件中作為數據源的列相對應。因此,如果你想將數據保存到數據庫中時,要記得表達式不能保存進數據庫。

上文討論了SQL和ADO.NET中的表達式,演示了表達式所提供的廣泛的函數功能,你能在應用程序中使用它們生成和添加數據。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗