艾伟_转载:ASP.NET实现类似Excel的数据透视表

简介: 代码: /Files/zhuqil/Pivot.zip    数据透视表提供的数据三维视图效果,在Microsoft Excel能创建数据透视表,但是,它并不会总是很方便使用Excel。您可能希望在Web应用程序中创建一个数据透视报表。

    代码: /Files/zhuqil/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。

代码
///    
///  Creates an advanced 3D Pivot table. 
///    
///   The main heading at the top of the report.  
///   The heading on the left of the report.  
///   The sub heading at the top of the report.  
///   HtmlTable Control.  
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的定位。

使用默认样式的高级的数据透视表:

  John Jane Sally Sarah Grand Totals
Product Sale Amount Quantity Sale Amount Quantity Sale Amount Quantity Sale Amount Quantity Sale Amount Quantity
Pens 350 200 393.75 225 353.5 202 196 112 1293.25 739
Pencils 500 400 418.75 335 378.75 303 306.25 245 1603.75 1283
Notebooks 300 100 600 200 600 198 594 198 2094 696
Rulers 100 50 150 75 594 98 100 50 944 273
Calculators 1200 120 800 80 800 80 660 66 3460 346
Back Packs 1500 75 1940 97 2020 101 2020 50 7480 323
Totals 3950 945 4302.50 1012 4746.25 982 3876.25 721 16875.00 3660

使用自定义的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

 参考原文:http://www.codeproject.com/KB/aspnet/Pivot.aspx

目录
打赏
0
0
0
0
52
分享
相关文章
利用Python自动化处理Excel数据:从基础到进阶####
本文旨在为读者提供一个全面的指南,通过Python编程语言实现Excel数据的自动化处理。无论你是初学者还是有经验的开发者,本文都将帮助你掌握Pandas和openpyxl这两个强大的库,从而提升数据处理的效率和准确性。我们将从环境设置开始,逐步深入到数据读取、清洗、分析和可视化等各个环节,最终实现一个实际的自动化项目案例。 ####
387 10
从Excel到Hadoop:数据规模的进化之路
从Excel到Hadoop:数据规模的进化之路
39 10
自动化数据处理:使用Selenium与Excel打造的数据爬取管道
本文介绍了一种使用Selenium和Excel结合代理IP技术从WIPO品牌数据库(branddb.wipo.int)自动化爬取专利信息的方法。通过Selenium模拟用户操作,处理JavaScript动态加载页面,利用代理IP避免IP封禁,确保数据爬取稳定性和隐私性。爬取的数据将存储在Excel中,便于后续分析。此外,文章还详细介绍了Selenium的基本设置、代理IP配置及使用技巧,并探讨了未来可能采用的更多防反爬策略,以提升爬虫效率和稳定性。
296 4
招行面试:100万级别数据的Excel,如何秒级导入到数据库?
本文由40岁老架构师尼恩撰写,分享了应对招商银行Java后端面试绝命12题的经验。文章详细介绍了如何通过系统化准备,在面试中展示强大的技术实力。针对百万级数据的Excel导入难题,尼恩推荐使用阿里巴巴开源的EasyExcel框架,并结合高性能分片读取、Disruptor队列缓冲和高并发批量写入的架构方案,实现高效的数据处理。此外,文章还提供了完整的代码示例和配置说明,帮助读者快速掌握相关技能。建议读者参考《尼恩Java面试宝典PDF》进行系统化刷题,提升面试竞争力。关注公众号【技术自由圈】可获取更多技术资源和指导。
不通过navicat工具怎么把查询数据导出到excel表中
不通过navicat工具怎么把查询数据导出到excel表中
74 0
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
这篇文章介绍了如何使用Python读取Excel文件中的数据,处理后将其保存为txt、xlsx和csv格式的文件。
286 3
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
入门指南:利用NHibernate简化.NET应用程序的数据访问
【10月更文挑战第13天】NHibernate是一个面向.NET的开源对象关系映射(ORM)工具,它提供了从数据库表到应用程序中的对象之间的映射。通过使用NHibernate,开发者可以专注于业务逻辑和领域模型的设计,而无需直接编写复杂的SQL语句来处理数据持久化问题。NHibernate支持多种数据库,并且具有高度的灵活性和可扩展性。
76 2
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
使用Python读取Excel数据
本文介绍了如何使用Python的`pandas`库读取和操作Excel文件。首先,需要安装`pandas`和`openpyxl`库。接着,通过`read_excel`函数读取Excel数据,并展示了读取特定工作表、查看数据以及计算平均值等操作。此外,还介绍了选择特定列、筛选数据和数据清洗等常用操作。`pandas`是一个强大且易用的工具,适用于日常数据处理工作。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等