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

圖 1:按天、月、年彙總的訂單合計
單一表單包含五個 DataGridView,分別顯示:
當使用者在第一個網格修改明細行數值,其餘四個網格的彙總值必須即時自動重算。這要求利用 DataColumn.Expression 與 DataRelation 建立跨表的級聯計算鏈。
以 SqlDataAdapter.Fill 載入 OrderLines 後,動態新增 ExtendedPrice 欄位:
ds.Tables["OrderLines"].Columns.Add("ExtendedPrice", typeof(decimal),
"(UnitPrice * Quantity) * (1 - Discount)");
此欄位值隨著 UnitPrice、Quantity、Discount 任一欄位變更而自動重算,無需撰寫事件處理程序。
先以 SQL 查詢載入 Orders 表(含 OrderID、OrderDate),再建立關聯:
ds.Relations.Add("Orders2OrderLines",
ds.Tables["Orders"].Columns["OrderID"],
ds.Tables["OrderLines"].Columns["OrderID"]);
orders.Columns.Add("OrderTotal", typeof(decimal),
"Sum(Child.ExtendedPrice)");
Child 關鍵字指向關聯的子表,Sum 聚合函數自動加總符合 OrderID 的所有子列 ExtendedPrice。若存在多重關聯,可明確指定:Sum(Child(Orders2OrderLines).ExtendedPrice)。
同樣邏輯疊加:
// 日彙總表
ds.Relations.Add("OrdersByDay2Orders",
ds.Tables["OrdersByDay"].Columns["OrderDate"],
ds.Tables["Orders"].Columns["OrderDate"]);
dayOrders.Columns.Add("OrderTotalByDay", typeof(decimal),
"Sum(Child.OrderTotal)");
// 月、年彙總表以此類推...
每一層皆為獨立 DataTable,透過關聯鏈將變更層層向上傳遞,實現「編輯明細 → 訂單小計即時更新 → 日/月/年彙總連動刷新」的完整響應鏈。
若需在明細行網格顯示該筆明細所屬訂單的總金額,可在 OrderLines 表加入:
ds.Tables["OrderLines"].Columns.Add("OrderTotalForOrderLinesTable", typeof(decimal),
"Parent.OrderTotal");
Parent 運算子沿著關聯向上查找父表對應列的值。因關聯雙向同步,父表 OrderTotal 變更會自動反映至子表此運算式欄位。常用於建立綁定至子表網格的 DataView,豐富明細檢視資訊。
資料庫回傳欄位順序往往不符 UI 需求。DataColumn.SetOrdinal 可在載入後重排:
// 將第 1 欄 (索引 0) 移至第 4 欄 (索引 3) myDataTable.Columns[0].SetOrdinal(3); // 或以名稱指定 myDataTable.Columns["OrderID"].SetOrdinal(3);
被插入位置之前的欄位索引自動遞減,無需重建 DataTable 或修改 SQL。
經典兩難:客戶清單頁面,是否應一次載入所有客戶完整資料?
| 策略 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 一次載入完整資料 | 後續切換客戶零延遲;減少資料庫往返 | 記憶體佔用大;資料易過時;冷啟動慢 | 資料量小(< 1 萬筆)、更新頻率低、離線操作 |
| 清單僅載 ID/名稱,按需載入明細 | 啟動快;記憶體輕量;資料永遠最新 | 每次切換需查詢;網路延遲可感知 | 資料量大、高併發、頻繁更新、行動裝置 |
決策關鍵指標:
一般建議:預設採「清單輕量 + 按需載入」,僅在經效能測試證實瓶頸於查詢往返時,才考慮預載或快取策略。
運算式欄位僅能逐列計算;若需對符合條件的列集合執行聚合(如:美國客戶的平均訂單金額),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 等機制,讓開發者能在記憶體中構建類似試算表的多層級、跨表、即時級聯計算模型,無需頻繁往返資料庫,亦無需撰寫大量事件處理代碼。關鍵要點:
Child/Parent 運算子實現跨表值傳遞與彙總善用這些內建功能,可大幅減少自定義計算代碼,提升開發效率與執行效能。