代碼:Pivot.zip
數據透視表提供的數據三維視圖效果,在Microsoft Excel能創建數據透視表,但是,它並不會總是很方便使用Excel。您可能希望在Web應用程序中創建一個數據透視報表。創建一個簡單的數據透視表可能是一件非常復雜的任務。所以,我打算不但為你提供一個非常有用的工具創建簡單和高級的數據透視表,而且為你移除一些籠罩他們的神秘面紗。
目標是:我們想要有能力將datatable中的二維的數據轉換成三維視圖。
在大多數情況下,你會從數據庫的查詢數據填充數據表,例如
以下為引用的內容:
SELECT
SalesPeople.FullName AS [Sales Person]
, Products.FullName AS [Product]
, SUM(Sales.SalesAmount) AS [Sale Amount]
, SUM(Sales.Qty) AS [Quantity]
FROM
Sales
JOIN
SalesPeople WITH (NOLOCK)
ON SalesPeople.SalesPersonID = Sales.SalesPersonID
JOIN
Products WITH (NOLOCK)
ON Products.ProductCode = Sales.ProductCode
GROUP BY
SalesPeople.FullName
, Products.FullName
該查詢會產生下面的數據表:
Sales Person
Product
Quantity
Sale Amount
John
Pens
200
350
John
Pencils
400
500
John
Notebooks
100
300
John
Rulers
50
100
John
Calculators
120
1200
John
Back Packs
75
1500
Jane
Pens
225
393.75
Jane
Pencils
335
418.75
Jane
Notebooks
200
600
Jane
Rulers
75
150
Jane
Calculators
80
800
Jane
Back Packs
97
1940
Sally
Pens
202
353.5
Sally
Pencils
303
378.75
Sally
Notebooks
198
600
Sally
Rulers
98
594
Sally
Calculators
80
800
Sally
Back Packs
101
2020
Sarah
Pens
112
196
Sarah
Pencils
245
306.25
Sarah
Notebooks
198
594
Sarah
Rulers
50
100
Sarah
Calculators
66
660
Sarah
Back Packs
50
2020
正如你所看到的,這是一個二維表,它不是一個非常有用的報表。因此,我們得改變,將它變成更可讀的數據表。
數據透視表有3個面:
X軸構成了在表格上方的大標題。Y軸構成表的左欄,Z軸構成了X軸和Y軸對應的值。簡單的數據透視表將會對每一個x軸值都只有一個z軸列,高級的數據透視表將對於每個X軸的值會對應有多個Z軸的值。
一個非常重要的一點是,Z軸的值只能是數字。這是因為Z軸值為橫軸和縱軸的總額。使用一個非數值Z軸字段將拋出一個異常。
因此,如果你注意上面的數據表,你會發現,“Sales Person”和“Product”字段可以分配到的X軸或Y軸,但不能給z軸。在“Quantity”和“Sale Amount”字段可以被分配到z軸。
Pivot 類將數據表轉換成Html table。然後您可以將它輸出到Web窗體上。那麼,這只是實現的方法。如果你願意,你可以根據這個類的邏輯創建一個用戶控件。
以下為引用的內容:
#region Variables
private DataTable _DataTable;
private string _CSSTopHeading;
private string _CSSSubHeading;
private string _CSSLeftColumn;
private string _CSSItems;
private string _CSSTotals;
private string _CSSTable;
#endregion Variables
#region Constructors
public Pivot(DataTable dataTable)
{
Init();
_DataTable = dataTable;
}
#endregion Constructors
這部分的代碼是非常自我解釋。 你能創建一個Pivot 對象,通過傳遞一個datatable作為參數。在init()方法只分配一個空字符串值給CSS變量。如果CSS的變量是一個空字符串,構造方法將使用默認的樣式。每一個CSS變量都有一個相應的屬性。
以下為引用的內容:
private string FindValue(string xAxisField, string xAxisValue, string yAxisField, string yAxisValue, string zAxisFIEld)
{
string zAxisValue = "";
try
{
foreach (DataRow row in _DataTable.Rows)
{
if (Convert.ToString(row[xAxisField]) == xAxisValue && Convert.ToString(row[yAxisFIEld]) == yAxisValue)
{
zAxisValue = Convert.ToString(row[zAxisFIEld]);
break;
}
}
}
catch
{
throw;
}
return zAxisValue;
}
在FindValue(...)方法在數據表中搜索的對應x軸和y軸值的Z軸值。xAxisField是X軸字段的列名(例如“Product”),而xAxisValue是在該列的值。該yAxisField是的Y軸字段的列名(例如“Sales Person”),並yAxisValue是在該列的值。該zAxisFIEld是列名,在其中Z軸值,是您正在尋找地(例如“Sale Amount”)。
以下為引用的內容:
private string[] FindValues(string xAxisField, string xAxisValue, string yAxisField, string yAxisValue, string[] zAxisFIElds)
{
int zAxis = zAxisFIElds.Length;
if (zAxis < 1)
zAxis++;
string[] zAxisValues = new string[zAxis];
//set default values
for (int i = 0; i <= zAxisValues.GetUpperBound(0); i++)
{
zAxisValues[i] = "0";
}
try
{
foreach (DataRow row in _DataTable.Rows)
{
if (Convert.ToString(row[xAxisField]) == xAxisValue && Convert.ToString(row[yAxisFIEld]) == yAxisValue)
{
for (int z = 0; z < zAxis; z++)
{
zAxisValues[z] = Convert.ToString(row[zAxisFIElds[z]]);
}
break;
}
}
}
catch
{
throw;
}
return zAxisValues;
}
在FindValues(...)方法類似FindValue(...)方法,然而,它會返回多個z軸的值。這是用於高級的數據透視表,對應於x軸的值,您會有多個Z軸列。
以下為引用的內容:
private void MainHeaderTopCellStyle(HtmlTableCell cell)
{
if (_CSSTopHeading == "")
{
cell.Style.Add("font-family", "tahoma");
cell.Style.Add("font-size", "10pt");
cell.Style.Add("font-weight", "normal");
cell.Style.Add("background-color", "black");
cell.Style.Add("color", "white");
cell.Style.Add("text-align", "center");
}
else
cell.Attributes.Add("Class", _CSSTopHeading);
}
這是CSS樣式的方法之一。這在X軸上使用流行的樣式(table的頂行)。如果您沒有指定一個CSS類名給這個屬性,該方法將使用默認的樣式。 CSS類將會被應用到網頁中的Html table。
以下為引用的內容:
/// <summary>
/// Creates an advanced 3D Pivot table.
/// </summary>
/// <param name="xAxisFIEld">The main heading at the top of the report.</param>
/// <param name="yAxisFIEld">The heading on the left of the report.</param>
/// <param name="zAxisFIElds">The sub heading at the top of the report.</param>
/// <returns>HtmlTable Control.</returns>
public HtmlTable PivotTable(string xAxisField, string yAxisField, string[] zAxisFIElds)
{
HtmlTable table = new HtmlTable();
//style table
TableStyle(table);
/*
* The x-axis is the main horizontal row.
* The z-axis is the sub horizontal row.
* The y-axis is the left vertical column.
*/
try
{
//get distinct xAxisFIElds
ArrayList xAxis = new ArrayList();
foreach (DataRow row in _DataTable.Rows)
{
if (!xAxis.Contains(row[xAxisFIEld]))
xAxis.Add(row[xAxisFIEld]);
}
//get distinct yAxisFIElds
ArrayList yAxis = new ArrayList();
foreach (DataRow row in _DataTable.Rows)
{
if (!yAxis.Contains(row[yAxisFIEld]))
yAxis.Add(row[yAxisFIEld]);
}
//create a 2D array for the y-axis/z-axis fIElds
int zAxis = zAxisFIElds.Length;
if (zAxis < 1)
zAxis = 1;
string[,] matrix = new string[(xAxis.Count * zAxis), yAxis.Count];
string[] zAxisValues = new string[zAxis];
for (int y = 0; y < yAxis.Count; y++) //loop thru y-axis fIElds
{
//rows
for (int x = 0; x < xAxis.Count; x++) //loop thru x-axis fIElds
{
//main columns
//get the z-axis values
zAxisValues = FindValues(xAxisFIEld, Convert.ToString(xAxis[x])
, yAxisField, Convert.ToString(yAxis[y]), zAxisFIElds);
for (int z = 0; z < zAxis; z++) //loop thru z-axis fIElds
{
//sub columns
matrix[(((x + 1) * zAxis - zAxis) + z), y] = zAxisValues[z];
}
}
}
//calculate totals for the y-axis
decimal[] yTotals = new decimal[(xAxis.Count * zAxis)];
for (int col = 0; col < (xAxis.Count * zAxis); col++)
{
yTotals[col] = 0;
for (int row = 0; row < yAxis.Count; row++)
{
yTotals[col] += Convert.ToDecimal(matrix[col, row]);
}
}
//calculate totals for the x-axis
decimal[,] xTotals = new decimal[zAxis, (yAxis.Count + 1)];
for (int y = 0; y < yAxis.Count; y++) //loop thru the y-axis
{
int zCount = 0;
for (int z = 0; z < (zAxis * xAxis.Count); z++) //loop thru the z-axis
{
xTotals[zCount, y] += Convert.ToDecimal(matrix[z, y]);
if (zCount == (zAxis - 1))
zCount = 0;
else
zCount++;
}
}
for (int xx = 0; xx < zAxis; xx++) //Grand Total
{
for (int xy = 0; xy < yAxis.Count; xy++)
{
xTotals[xx, yAxis.Count] += xTotals[xx, xy];
}
}
//Build Html Table
//Append main row (x-axis)
HtmlTableRow mainRow = new HtmlTableRow();
mainRow.Cells.Add(new HtmlTableCell());
for (int x = 0; x <= xAxis.Count; x++) //loop thru x-axis + 1
{
HtmlTableCell cell = new HtmlTableCell();
cell.ColSpan = zAxis;
if (x < xAxis.Count)
cell.InnerText = Convert.ToString(xAxis[x]);
else
cell.InnerText = "Grand Totals";
//style cell
MainHeaderTopCellStyle(cell);
mainRow.Cells.Add(cell);
}
table.Rows.Add(mainRow);
//Append sub row (z-axis)
HtmlTableRow subRow = new HtmlTableRow();
subRow.Cells.Add(new HtmlTableCell());
subRow.Cells[0].InnerText = yAxisFIEld;
//style cell
SubHeaderCellStyle(subRow.Cells[0]);
for (int x = 0; x <= xAxis.Count; x++) //loop thru x-axis + 1
{
for (int z = 0; z < zAxis; z++)
{
HtmlTableCell cell = new HtmlTableCell();
cell.InnerText = zAxisFIElds[z];
//style cell
SubHeaderCellStyle(cell);
subRow.Cells.Add(cell);
}
}
table.Rows.Add(subRow);
//Append table items from matrix
for (int y = 0; y < yAxis.Count; y++) //loop thru y-axis
{
HtmlTableRow itemRow = new HtmlTableRow();
for (int z = 0 ; z <= (zAxis * xAxis.Count); z++) //loop thru z-axis + 1
{
HtmlTableCell cell = new HtmlTableCell();
if (z == 0)
{
cell.InnerText = Convert.ToString(yAxis[y]);
//style cell
MainHeaderLeftCellStyle(cell);
}
else
{
cell.InnerText = Convert.ToString(matrix[(z-1), y]);
//style cell
ItemCellStyle(cell);
}
itemRow.Cells.Add(cell);
}
//append x-axis grand totals
for (int z = 0; z < zAxis; z++)
{
HtmlTableCell cell = new HtmlTableCell();
cell.InnerText = Convert.ToString(xTotals[z, y]);
//style cell
TotalCellStyle(cell);
itemRow.Cells.Add(cell);
}
table.Rows.Add(itemRow);
}
//append y-axis totals
HtmlTableRow totalRow = new HtmlTableRow();
for (int x = 0; x <= (zAxis * xAxis.Count); x++)
{
HtmlTableCell cell = new HtmlTableCell();
if (x == 0)
cell.InnerText = "Totals";
else
cell.InnerText = Convert.ToString(yTotals[x-1]);
//style cell
TotalCellStyle(cell);
totalRow.Cells.Add(cell);
}
//append x-axis/y-axis totals
for (int z = 0; z < zAxis; z++)
{
HtmlTableCell cell = new HtmlTableCell();
cell.InnerText = Convert.ToString(xTotals[z, xTotals.GetUpperBound(1)]);
//style cell
TotalCellStyle(cell);
totalRow.Cells.Add(cell);
}
table.Rows.Add(totalRow);
}
catch
{
throw;
}
return table;
}
PivotTable(…) 方法,是所有神奇發生的地方。有兩種重載方法,一個創建了一個簡單的數據透視表,而其他(上面的方法)創建一個高級的數據透視表。唯一的區別在於,一個簡單只有一個的z軸,而高級的,不止一個。
Pivot.zip文件中包括兩個解決方案。Pivot 是一個類庫解決方案是。您可以編譯此解決方案和在Web應用程序中引用Pivot.dll。另一個解決方案是PivotTest,它是是一個ASP.Net應用程序。這說明如何實現Pivot類。
以下為引用的內容:
public DataTable DataTableForTesting
{
get
{
DataTable dt = new DataTable("Sales Table");
dt.Columns.Add("Sales Person");
dt.Columns.Add("Product");
dt.Columns.Add("Quantity");
dt.Columns.Add("Sale Amount");
dt.Rows.Add(new object[] { "John", "Pens", 200, 350.00 });
dt.Rows.Add(new object[] { "John", "Pencils", 400, 500.00 });
dt.Rows.Add(new object[] { "John", "Notebooks", 100, 300.00 });
dt.Rows.Add(new object[] { "John", "Rulers", 50, 100.00 });
dt.Rows.Add(new object[] { "John", "Calculators", 120, 1200.00 });
dt.Rows.Add(new object[] { "John", "Back Packs", 75, 1500.00 });
dt.Rows.Add(new object[] { "Jane", "Pens", 225, 393.75 });
dt.Rows.Add(new object[] { "Jane", "Pencils", 335, 418.75 });
dt.Rows.Add(new object[] { "Jane", "Notebooks", 200, 600.00 });
dt.Rows.Add(new object[] { "Jane", "Rulers", 75, 150.00 });
dt.Rows.Add(new object[] { "Jane", "Calculators", 80, 800.00 });
dt.Rows.Add(new object[] { "Jane", "Back Packs", 97, 1940.00 });
dt.Rows.Add(new object[] { "Sally", "Pens", 202, 353.50 });
dt.Rows.Add(new object[] { "Sally", "Pencils", 303, 378.75 });
dt.Rows.Add(new object[] { "Sally", "Notebooks", 198, 600.00 });
dt.Rows.Add(new object[] { "Sally", "Rulers", 98, 594.00 });
dt.Rows.Add(new object[] { "Sally", "Calculators", 80, 800.00 });
dt.Rows.Add(new object[] { "Sally", "Back Packs", 101, 2020.00 });
dt.Rows.Add(new object[] { "Sarah", "Pens", 112, 196.00 });
dt.Rows.Add(new object[] { "Sarah", "Pencils", 245, 306.25 });
dt.Rows.Add(new object[] { "Sarah", "Notebooks", 198, 594.00 });
dt.Rows.Add(new object[] { "Sarah", "Rulers", 50, 100.00 });
dt.Rows.Add(new object[] { "Sarah", "Calculators", 66, 660.00 });
dt.Rows.Add(new object[] { "Sarah", "Back Packs", 50, 2020.00 });
return dt;
}
}
我已創建數據表的屬性,它建立在上面的例子中的數據表。這只是用於演示目的。
以下為引用的內容:
protected void Page_Load(object sender, EventArgs e)
{
//Advanced Pivot
Pivot advPivot = new Pivot(DataTableForTesting);
HtmlTable advancedPivot = advPivot.PivotTable("Sales Person", "Product", new string[] { "Sale Amount", "Quantity" });
div1.Controls.Add(advancedPivot);
//Simple Pivot
Pivot pivot = new Pivot(DataTableForTesting);
//override default style with CSS
pivot.CSSTopHeading = "Heading";
pivot.CSSLeftColumn = "LeftColumn";
pivot.CSSItems = "Items";
pivot.CSSTotals = "Totals";
pivot.CSSTable = "Table";
HtmlTable simplePivot = pivot.PivotTable("Product", "Sales Person", "Sale Amount");
div2.Controls.Add(simplePivot);
}
上述代碼包括兩個實例化的pivot對象。第一個高級的pivot和第二是一個簡單的pivot。你可以看到我已經為div添加了HtmlTable控件。我創建具有runat="server"屬性的div,這樣我可以在後台代碼裡面訪問它。div只是幫助HtmlTable的定位。
使用默認樣式的高級的數據透視表:
運行代碼:
<table >
<tbody>
<tr>
<td></td>
<td colSpan="2">John</td>
<td colSpan="2">Jane</td>
<td colSpan="2">Sally</td>
<td colSpan="2">Sarah</td>
<td colSpan="2">Grand Totals</td></tr>
<tr>
<td >Product</td>
<td >Sale Amount</td>
<td >Quantity</td>
<td >Sale Amount</td>
<td >Quantity</td>
<td >Sale Amount</td>
<td >Quantity</td>
<td >Sale Amount</td>
<td >Quantity</td>
<td >Sale Amount</td>
<td >Quantity</td></tr>
<tr>
<td >Pens</td>
<td >350</td>
<td >200</td>
<td >393.75</td>
<td >225</td>
<td >353.5</td>
<td >202</td>
<td >196</td>
<td >112</td>
<td >1293.25</td>
<td >739</td></tr>
<tr>
<td >Pencils</td>
<td >500</td>
<td >400</td>
<td >418.75</td>
<td >335</td>
<td >378.75</td>
<td >303</td>
<td >306.25</td>
<td >245</td>
<td >1603.75</td>
<td >1283</td></tr>
<tr>
<td >Notebooks</td>
<td >300</td>
<td >100</td>
<td >600</td>
<td >200</td>
<td >600</td>
<td >198</td>
<td >594</td>
<td >198</td>
<td >2094</td>
<td >696</td></tr>
<tr>
<td >Rulers</td>
<td >100</td>
<td >50</td>
<td >150</td>
<td >75</td>
<td >594</td>
<td >98</td>
<td >100</td>
<td >50</td>
<td >944</td>
<td >273</td></tr>
<tr>
<td >Calculators</td>
<td >1200</td>
<td >120</td>
<td >800</td>
<td >80</td>
<td >800</td>
<td >80</td>
<td >660</td>
<td >66</td>
<td >3460</td>
<td >346</td></tr>
<tr>
<td >Back Packs</td>
<td >1500</td>
<td >75</td>
<td >1940</td>
<td >97</td>
<td >2020</td>
<td >101</td>
<td >2020</td>
<td >50</td>
<td >7480</td>
<td >323</td></tr>
<tr>
<td >Totals</td>
<td >3950</td>
<td >945</td>
<td >4302.50</td>
<td >1012</td>
<td >4746.25</td>
<td >982</td>
<td >3876.25</td>
<td >721</td>
<td >16875.00</td>
<td >3660</td></tr></tbody></table>
使用自定義的CSS樣式簡單的數據透視表:
Sales Person
Pens
Pencils
Notebooks
Rulers
Calculators
Back Packs
Grand Totals
John
350
500
300
100
1200
1500
3950
Jane
393.75
418.75
600
150
800
1940
4302.50
Sally
353.5
378.75
600
594
800
2020
4746.25
Sarah
196
306.25
594
100
660
2020
3876.25
Totals
1293.25
1603.75
2094
944
3460
7480
16875.00
代碼:Pivot.zip