需求:書店銷售報表,列為作者,欄為書店 (每月新增/關閉),值為銷售額彙總。欄位數量動態變化。
CREATE PROC usp_SalesCrosstab AS
BEGIN
DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX);
-- 1. 動態產生欄位清單 (CASE 運算式)
SELECT @cols = COALESCE(@cols + ',', '') +
'SUM(CASE WHEN Store = ' + QUOTENAME(Store, '''') + ' THEN Sales ELSE 0 END) AS ' + QUOTENAME(Store)
FROM (SELECT DISTINCT Store FROM vwSales2) AS S;
-- 2. 組裝完整查詢
SET @sql = 'SELECT Author, ' + @cols + ' FROM vwSales2 GROUP BY Author ORDER BY Author;';
-- 3. 執行
EXEC sp_executesql @sql;
END
// 1. 取得扁平資料 (Author, Store, Sales)
DataTable dtFlat = GetFlatSalesData();
// 2. 建立 DataSet 關聯 (可選,單表亦可)
DataSet ds = new DataSet();
ds.Tables.Add(dtFlat);
// 3. 使用 DataView / LINQ / 自訂演算法 樞轉
var pivot = dtFlat.AsEnumerable()
.GroupBy(r => r.Field("Author"))
.Select(g => new {
Author = g.Key,
Stores = g.ToDictionary(r => r.Field("Store"), r => r.Field("Sales"))
}).ToList();
// 4. 動態建立報表 DataTable (供 DataGrid/GridView 繫結)
DataTable dtReport = new DataTable();
dtReport.Columns.Add("Author");
foreach (var store in allStores) dtReport.Columns.Add(store, typeof(decimal));
foreach (var row in pivot) {
var dr = dtReport.NewRow();
dr["Author"] = row.Author;
foreach (var kv in row.Stores) dr[kv.Key] = kv.Value;
dtReport.Rows.Add(dr);
}
| 因素 | 動態 SQL (DB 層) | ADO.NET 轉換 (App 層) |
|---|---|---|
| 資料量 (明細列數) | 大 (> 10 萬列) | 中小 (< 5 萬列) |
| 欄位動態性 | 高 (頻繁新增店點) | 高 (同樣適用) |
| 用戶端類型 | 多元 (報表伺服器、Excel、舊系統) | .NET 應用 (WinForm, WebForm, ASP.NET Core) |
| 開發維護成本 | 較高 (T-SQL 動態字串) | 較低 (C# 強型別) |
| 效能調優彈性 | 索引、平行查詢、計畫引導 | 記憶體、平行 PLINQ、快取 |