首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

ADO.NET 進階數據操作:運算式欄位、DataRelation 與 Compute 方法實戰

     編輯:關於.NET

ADO.NET 不僅是資料存取工具,其離線架構中的 DataTable、DataColumn、DataRelation 更提供豐富的記憶體內運算能力。本文透過一個多層級訂單統計儀表板範例,深入解析如何運用運算式欄位、跨表關聯彙總、Compute 動態計算、SetOrdinal 欄位重排,以及資料載入策略權衡,構建高效能、即時回應的數據分析介面。

案例場景:多層級訂單統計儀表板

圖 1:按天、月、年彙總的訂單合計

單一表單包含五個 DataGridView,分別顯示:

  1. 訂單明細行(可編輯單價、數量、折扣)
  2. 訂單小計(每筆訂單的擴展價格總和)
  3. 每日彙總
  4. 每月彙總
  5. 每年彙總

當使用者在第一個網格修改明細行數值,其餘四個網格的彙總值必須即時自動重算。這要求利用 DataColumn.Expression 與 DataRelation 建立跨表的級聯計算鏈。

核心技術:運算式欄位與 DataRelation 串聯

步驟 1:明細表加入擴展價格運算式欄位

以 SqlDataAdapter.Fill 載入 OrderLines 後,動態新增 ExtendedPrice 欄位:

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

此欄位值隨著 UnitPrice、Quantity、Discount 任一欄位變更而自動重算,無需撰寫事件處理程序。

步驟 2:建立訂單表與明細表的 DataRelation

先以 SQL 查詢載入 Orders 表(含 OrderID、OrderDate),再建立關聯:

ds.Relations.Add("Orders2OrderLines",
    ds.Tables["Orders"].Columns["OrderID"],
    ds.Tables["OrderLines"].Columns["OrderID"]);

步驟 3:訂單表加入 OrderTotal 運算式欄位

orders.Columns.Add("OrderTotal", typeof(decimal),
    "Sum(Child.ExtendedPrice)");

Child 關鍵字指向關聯的子表,Sum 聚合函數自動加總符合 OrderID 的所有子列 ExtendedPrice。若存在多重關聯,可明確指定:Sum(Child(Orders2OrderLines).ExtendedPrice)。

步驟 4:逐層向上彙總——日、月、年統計表

同樣邏輯疊加:

// 日彙總表
ds.Relations.Add("OrdersByDay2Orders",
    ds.Tables["OrdersByDay"].Columns["OrderDate"],
    ds.Tables["Orders"].Columns["OrderDate"]);
dayOrders.Columns.Add("OrderTotalByDay", typeof(decimal),
    "Sum(Child.OrderTotal)");

// 月、年彙總表以此類推...

每一層皆為獨立 DataTable,透過關聯鏈將變更層層向上傳遞,實現「編輯明細 → 訂單小計即時更新 → 日/月/年彙總連動刷新」的完整響應鏈。

反向導航:Parent 運算子將父表值帶入子表

若需在明細行網格顯示該筆明細所屬訂單的總金額,可在 OrderLines 表加入:

ds.Tables["OrderLines"].Columns.Add("OrderTotalForOrderLinesTable", typeof(decimal),
    "Parent.OrderTotal");

Parent 運算子沿著關聯向上查找父表對應列的值。因關聯雙向同步,父表 OrderTotal 變更會自動反映至子表此運算式欄位。常用於建立綁定至子表網格的 DataView,豐富明細檢視資訊。

實用技巧:SetOrdinal 動態調整欄位顯示順序

資料庫回傳欄位順序往往不符 UI 需求。DataColumn.SetOrdinal 可在載入後重排:

// 將第 1 欄 (索引 0) 移至第 4 欄 (索引 3)
myDataTable.Columns[0].SetOrdinal(3);
// 或以名稱指定
myDataTable.Columns["OrderID"].SetOrdinal(3);

被插入位置之前的欄位索引自動遞減,無需重建 DataTable 或修改 SQL。

資料載入策略:一次大查詢 vs. 多次小查詢

經典兩難:客戶清單頁面,是否應一次載入所有客戶完整資料?

策略優點缺點適用場景
一次載入完整資料後續切換客戶零延遲;減少資料庫往返記憶體佔用大;資料易過時;冷啟動慢資料量小(< 1 萬筆)、更新頻率低、離線操作
清單僅載 ID/名稱,按需載入明細啟動快;記憶體輕量;資料永遠最新每次切換需查詢;網路延遲可感知資料量大、高併發、頻繁更新、行動裝置

決策關鍵指標:

  • 資料總量與單筆大小
  • 使用者實際存取比例(是否大部分客戶從不被點擊)
  • 資料時效性要求
  • 網路頻寬與延遲

一般建議:預設採「清單輕量 + 按需載入」,僅在經效能測試證實瓶頸於查詢往返時,才考慮預載或快取策略。

動態彙總計算:DataTable.Compute 方法

運算式欄位僅能逐列計算;若需對符合條件的列集合執行聚合(如:美國客戶的平均訂單金額),Compute 是最佳利器:

object result = dataTable.Compute("Avg(OrderTotal)", "Country = 'USA'");

圖 2:使用 Compute 方法的動態計算介面

介面提供篩選欄位、運算子、條件值,以及聚合函數與計算欄位下拉選單。核心代碼:

private void btnCalculate_Click(object sender, EventArgs e)
{
    if (!ValidateControls()) return;

    var col = (DataColumn)ddlFilterColumn.SelectedItem;
    string filterValue = txtFilterCriteria.Text;

    // 字串與日期需加單引號
    if (col.DataType == typeof(string) || col.DataType == typeof(DateTime))
        filterValue = $"'{filterValue}'";

    // Like 運算子特殊處理
    if (ddlFilterOperator.Text == "Like")
        filterValue = $"'{txtFilterCriteria.Text}%'";

    string filter = $"{ddlFilterColumn.Text} {ddlFilterOperator.Text} {filterValue}";
    string expression = $"{ddlAggregate.Text}({ddlCalculationColumn.Text})";

    object value = orders.Compute(expression, filter);
    txtResult.Text = value.ToString();
}

支援七大聚合函數:Sum、Count、Avg、Min、Max、StDev、Var。篩選語法與 DataView.RowFilter 相同,支援 AND/OR/IN/LIKE 等運算子。

總結

ADO.NET 離線模型提供的運算式欄位、DataRelation、Compute 等機制,讓開發者能在記憶體中構建類似試算表的多層級、跨表、即時級聯計算模型,無需頻繁往返資料庫,亦無需撰寫大量事件處理代碼。關鍵要點:

  • 運算式欄位定義單列計算邏輯,自動追蹤相依欄位變更
  • DataRelation 串聯父子表,Child/Parent 運算子實現跨表值傳遞與彙總
  • Compute 方法提供臨時、條件式聚合計算,適合報表與儀表板動態查詢
  • SetOrdinal 解決欄位顯示順序與資料庫結構不一致的問題
  • 資料載入策略需依據資料量、時效性、使用模式權衡,預設傾向「輕量清單 + 按需明細」

善用這些內建功能,可大幅減少自定義計算代碼,提升開發效率與執行效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved