效率最高的Excel數據導入


本文目錄:

   (一)背景

   (二)數據庫數據導入到Excel的方法比較
   (三)SSIS的簡介
   (四)數據庫中存儲過程示例(SSIS應用需要)

   (五)Excel模板的制作(這步這么簡單,稍微介紹一下)
   (六)SSIS操作過程(生成Package,用來調用)(下一篇隨筆將詳細講解制作Package包的過程,圖片太多,篇幅過長,因此本文將直接采用生成的Package包進行應用)

   (七)C#中如何調用SSIS創建的Package和Excel模板(可以自己編寫邏輯代碼進行重復利用),用來生成Excel數據

   (八)總結

(一)背景

     如何將數據庫中的數據導入到EXCEL文件中,我們經常會碰到。本文將比較常用的幾種方法,並且將詳細講解基於SSIS的用法。筆者認為,基於SSIS的方法,對於海量數據來說,應該是效率最好的一種方法。個人認為,這是一種值得推薦的方法,因此,本人決定將本人所知道的、以及自己總結的完整的寫出來,一是提高一下自己的寫作以及表達能力,二是讓更多的讀者能夠在具體的應用中如何解決將海量數據導入到Excel中的效率問題。

 

(二)方法的比較 

    方案一:SSIS(SQL Server數據集成服務),追求效率,Package制作過程復雜一點(容易出錯)。

    方案二:采用COM.Excel組件。一般,對於操作能夠基本滿足,但對於數據量大時可能會慢點。下面的代碼,本人稍微修改了下,如下所示:該方法主要是對單元格一個一個的循環寫入,基本方法為 excel.WriteValue(ref vt, ref cf, ref ca, ref chl, ref rowIndex, ref colIndex, ref str, ref cellformat)。當數據量大時,肯定效率還是有影響的。

  public   string  DataExcels(System.Data.DataTable[] dts,  string  strTitle,  string  FilePath, Hashtable nameList, string [] titles)
        {
            COM.Excel.cExcelFile excel 
=   new  COM.Excel.cExcelFile();
            
// 當文件大於10的時候   清空所有文件!!!
            ClearFile(FilePath);
            
// 文件名
             string  filename  =  strTitle +  DateTime.Now.ToString( " yyyyMMddHHmmssff " +   " .xls " ;
            
// 生成相應的文件
            excel.CreateFile(FilePath  +  filename);
            
// 設置margin
            COM.Excel.cExcelFile.MarginTypes mt1  =  COM.Excel.cExcelFile.MarginTypes.xlsTopMargin;
            COM.Excel.cExcelFile.MarginTypes mt2 
=  COM.Excel.cExcelFile.MarginTypes.xlsLeftMargin;
            COM.Excel.cExcelFile.MarginTypes mt3 
=  COM.Excel.cExcelFile.MarginTypes.xlsRightMargin;
            COM.Excel.cExcelFile.MarginTypes mt4 
=  COM.Excel.cExcelFile.MarginTypes.xlsBottomMargin;
            
double  height  =   2.2 ;
            excel.SetMargin(
ref  mt1,  ref  height);
            excel.SetMargin(
ref  mt2,  ref  height);
            excel.SetMargin(
ref  mt3,  ref  height);
            excel.SetMargin(
ref  mt4,  ref  height);
            
// 設置字體!!
            COM.Excel.cExcelFile.FontFormatting ff  =  COM.Excel.cExcelFile.FontFormatting.xlsNoFormat;
            
string  font  =   " 宋體 " ;
            
short  fontsize  =   14 ;
            excel.SetFont(
ref  font,  ref  fontsize,  ref  ff);
            
byte  b1  =   1 , b2  =   12 ;
            
short  s3  =   12 ;
            excel.SetColumnWidth(
ref  b1,  ref  b2,  ref  s3);

            
string  header  =   " 頁眉 " ;
            
string  footer  =   " 頁腳 " ;
            excel.SetHeader(
ref  header);
            excel.SetFooter(
ref  footer);

            COM.Excel.cExcelFile.ValueTypes vt 
=  COM.Excel.cExcelFile.ValueTypes.xlsText;
            COM.Excel.cExcelFile.CellFont cf 
=  COM.Excel.cExcelFile.CellFont.xlsFont0;
            COM.Excel.cExcelFile.CellAlignment ca 
=  COM.Excel.cExcelFile.CellAlignment.xlsCentreAlign;
            COM.Excel.cExcelFile.CellHiddenLocked chl 
=  COM.Excel.cExcelFile.CellHiddenLocked.xlsNormal;
            
//  報表標題
             int  cellformat  =   1 ;      
            
int  rowIndex  =   1 ; // 起始行
             int  colIndex  =   0 ;
            
foreach  (System.Data.DataTable dt  in  dts)
            {               
                colIndex 
=   0 ;
                
// 取得列標題                
                 foreach  (DataColumn colhead  in  dt.Columns)
                {
                    colIndex
++ ;
                    
string  name  =  colhead.ColumnName.Trim();
                    
object  namestr  =  ( object )name;
                    excel.WriteValue(
ref  vt,  ref  cf,  ref  ca,  ref  chl,  ref  rowIndex,  ref  colIndex,  ref  namestr,  ref  cellformat);
                }
                
// 取得表格中的數據            
                 foreach  (DataRow row  in  dt.Rows)
                {
                    rowIndex
++ ;
                    colIndex 
=   0 ;
                    
foreach  (DataColumn col  in  dt.Columns)
                    {
                        colIndex
++ ;
                        
if  (col.DataType  ==  System.Type.GetType( " System.DateTime " ))
                        {     
                            
object  str  =  ( object )(Convert.ToDateTime(row[col.ColumnName].ToString())).ToString( " yyyy-MM-dd " ); ;
                            excel.WriteValue(
ref  vt,  ref  cf,  ref  ca,  ref  chl,  ref  rowIndex,  ref  colIndex,  ref  str,  ref  cellformat);
                        }
                        
else
                        {
                            
object  str  =  ( object )row[col.ColumnName].ToString();
                            excel.WriteValue(
ref  vt,  ref  cf,  ref  ca,  ref  chl,  ref  rowIndex,  ref  colIndex,  ref  str,  ref  cellformat);
                        }
                    }
                }
                rowIndex 
+=   3  ;
            }
            
int  ret  =  excel.CloseFile();          
            
return  FilePath + filename;
        }

 

 

    方案三:采用Excel組件。一般,對於操作能夠基本滿足,但對於數據量大時可能會慢點。下面的代碼,本人在原有基礎上稍微修改了下,如下所示:

 1  public   string  OutputExceles( string  strTitle,  string  FilePath,  string  typeName, System.Data.DataTable[] dtList,  string [] smallTitleList) 
 2          {
 3              beforeTime  =  DateTime.Now;
 4              Excel.Application excel;
 5              Excel._Workbook xBk;
 6              Excel._Worksheet xSt;
 7               int  rowIndex  =   1 ;
 8               int  colIndex  =   1 ;
 9              excel  =   new  Excel.ApplicationClass();
10              xBk  =  excel.Workbooks.Add( true );
11              xSt  =  (Excel._Worksheet)xBk.ActiveSheet;
12               int  add = 0 ;
13               foreach  (System.Data.DataTable dt  in  dtList)
14              {
15                  colIndex  =   1 ;               
16                   // 取得整個報表的標題            
17                  excel.Cells[rowIndex ,  1 =  smallTitle[add];
18                  add ++ ;
19                   /// /設置整個報表的標題格式            
20                  xSt.get_Range(excel.Cells[rowIndex,  1 ], excel.Cells[rowIndex , dt.Columns.Count]).Font.Bold  =   true ;
21                  xSt.get_Range(excel.Cells[rowIndex,  1 ], excel.Cells[rowIndex , dt.Columns.Count]).Font.Size  =   22 ;
22                   /// /設置整個報表的標題為跨列居中            
23                  xSt.get_Range(excel.Cells[rowIndex ,  1 ], excel.Cells[rowIndex , dt.Columns.Count]).Select();
24                  xSt.get_Range(excel.Cells[rowIndex ,  1 ], excel.Cells[rowIndex, dt.Columns.Count]).HorizontalAlignment  =  Excel.XlHAlign.xlHAlignCenterAcrossSelection;
25                  rowIndex ++ ;
26                   foreach  (DataColumn col  in  dt.Columns)
27                  {
28                      excel.Cells[rowIndex, colIndex]  =  col.ColumnName;
29                       // 設置標題格式為居中對齊
30                      xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).Font.Bold  =   true ;
31                      xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).HorizontalAlignment  =  Excel.XlVAlign.xlVAlignCenter;
32                      xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).Select();
33                      xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).Interior.ColorIndex  =  titleColorindex;
34                      colIndex ++ ;
35                  }
36                   // 取得表格中的數據            
37                   foreach  (DataRow row  in  dt.Rows)
38                  {
39                      rowIndex ++ ;
40                      colIndex  =   1 ;
41                       foreach  (DataColumn col  in  dt.Columns)
42                      {
43                           if  (col.DataType  ==  System.Type.GetType( " System.DateTime " ))
44                          {
45                               if  ( ! string .IsNullOrEmpty(row[col.ColumnName].ToString()))
46                              {
47                                  excel.Cells[rowIndex, colIndex]  =  (Convert.ToDateTime(row[col.ColumnName].ToString())).ToString( " yyyy-MM-dd " );
48                                  xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).HorizontalAlignment  =  Excel.XlVAlign.xlVAlignCenter;                           }
49                          }
50                           else   if  (col.DataType  ==  System.Type.GetType( " System.String " ))
51                          {
52                              excel.Cells[rowIndex, colIndex]  =   " ' "   +  row[col.ColumnName].ToString();
53                              xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).HorizontalAlignment  =  Excel.XlVAlign.xlVAlignCenter;r;                        }
54                           else
55                          {
56                              excel.Cells[rowIndex, colIndex]  =  row[col.ColumnName].ToString();
57                              xSt.get_Range(excel.Cells[rowIndex, colIndex], excel.Cells[rowIndex, colIndex]).HorizontalAlignment  =  Excel.XlVAlign.xlVAlignCenter;                        }
58                          colIndex ++ ;
59                      }
60                  }
61                  rowIndex  ++ ;
62              }
63              afterTime  =  DateTime.Now;
64              xSt.Name  =  strTitle;
65               string  filename  =  typeName  +  DateTime.Now.ToString( " yyyyMMdd " +   " .xls " ;
66              //  excel.Save(FilePath+filename);
67              excel.ActiveWorkbook.SaveCopyAs(FilePath  +  filename);          
68               #region   結束Excel進程
69              xBk.Close( null null null );
70              excel.Workbooks.Close();
71              excel.Quit();
72               #endregion
73               return  filename;
74          }      

 

    方法四:采用DataGrid,GridView自帶的屬性。如下:

private   void  ExportExcelFromDataGrid( string  filename, System.Web.UI.WebControls.GridView  ToExcelGrid)
        {
            Response.ClearHeaders();
            Response.Clear();
            Response.Expires 
=   0 ;
            Response.Buffer 
=   true ;
            Response.HeaderEncoding 
=  System.Text.Encoding.UTF8;
            
//   Response.Charset = "utf-8";
            Response.AppendHeader( " Content-Disposition " " attachment;filename= "   +  Server.UrlEncode(filename));
            Response.ContentEncoding 
=  System.Text.Encoding.Default; // 設置輸出流為簡體中文   
            
//   Response.ContentType = "application/ms-excel"; // 設置輸出文件類型為excel文件。  
            Response.ContentType  =   " Application/octet-stream " ;
            
this .EnableViewState  =   false ;
            System.Globalization.CultureInfo myCItrad 
=   new  System.Globalization.CultureInfo( " zh-CHS " true );
            System.IO.StringWriter oStringWriter 
=   new  System.IO.StringWriter(myCItrad);
            System.Web.UI.HtmlTextWriter oHtmlTextWriter 
=   new  System.Web.UI.HtmlTextWriter(oStringWriter);
            ToExcelGrid.RenderControl(oHtmlTextWriter);
            Response.Write(oStringWriter.ToString());
            Response.End();
        }


(三)SSIS的簡介
    SQL Server 2005 提供的一個集成化的商業智能開發平台,主要包括:  
  *SQL Server Analysis Services(SQL Server數據分析服務,簡稱SSAS)  
  *SQL Server Reporting Services(SQL Server報表服務,簡稱SSRS)  
  *SQL Server Integration Services(SQL Server數據集成服務,簡稱SSIS)

    SQL Server 2005 Integration Services (SSIS) 提供一系列支持業務應用程序開發的內置任務、容器、轉換和數據適配器。可以創建 SSIS 解決方案來使用 ETL 和商業智能解決復雜的業務問題,管理 SQL Server 數據庫以及在 SQL Server 實例之間復制 SQL Server 對象。
 
(四)數據庫中存儲過程示例(SSIS應用過程中需要的,最好拿個本子把需要的內容記下) 
    在SQL SERVER 2005中,以SSISDataBase數據庫作為應用,僅包括2張表City,Province.(主要是為了簡單,便於講解)

   

    其中存儲過程如下:

ALTER PROCEDURE  [dbo].[ProvinceSelectedCityInfo]
(
@provinceId  int = 0

as
begin
select P.EName 
as   省份拼音 ,P.CName  as   省份名 ,C.CName   as   城市名   from City C left join Province P
  on C.ProvinceId 
=  P.ProvinceId
where   C.ProvinceId  = @provinceId and @provinceId   is  not  null   or @provinceId  is   null  or  @provinceId = 0
end

 

    其中,在這一步中我們必須要記住相關的內容,如上標識(紅色);為什么這么做?主要是在制作SSIS包的時候很容易混淆,建議拿個本子把需要的內容寫好。

 
(五)Excel模板的制作(這步這么簡單,稍微介紹一下)
    因為SSIS中列映射對應的是Excel的標題,與數據是一對一的關系。先不管這么多,看下我們的模板,如下圖所示。我們應該能夠發現,省份拼音、省份名、城市名,還有ProvinceCityInfoExcel.xls,Sheet1都被筆者標識了,當然 這一步與數據庫中的存儲過程取出的數據也是一對一的。( 名稱一致,可以減少很多不必要的麻煩,不然的話,嘿嘿....自己去想,那不是哥的事)
 
     等下,需要將創建的EXCEL模板放置到我們的項目文件目錄中。 (詳見第七步)
 
(六)SSIS操作過程(生成Package,用來調用)
    這一步是最主要的過程,當然,也是很容易出錯的一步。 筆者會另外詳細介紹制作Package包的過程,本文將直接將生成的包放到VS項目中進行運用。
    利用SQL Server 2005數據庫自帶的SQL Server Business Intelligence Development Studio(SQL Server商業智能開發平台),最終生成的項目如下圖所示:
 
    然后, 將在SSIS項目中生成的Package.dtsx包復制到自己的項目文件目錄中。這就是我們馬上進入的步驟了---->(步驟七)
 
(七)C#中調用SSIS創建的Package和Excel模板(可以自己編寫邏輯代碼進行重復利用),用來生成Excel數據
    先看下我們的VS2008項目,如下圖所示:
 
    大家會發現,筆者 將(五)(六)步驟生成的模板和Package包放置在項目中的“Excel導出”目錄下,當然這些文件隨便你放在哪里,這是不用再廢話的,哈哈。
    另外,筆者簡單的設計了如下很粗糙的界面,目的是根據省份來顯示城市的相關信息(其實大家都是很熟悉這些的,很多項目都是有省-市-縣數據庫表的),添加一個導出按鈕,點擊的時候,我們可以 參考頁面顯示的內容和我們生成的客戶端Excel中的內容是否一致。
 
    現在我們的重頭戲開始了,如下代碼(點擊將觸發的代碼內容):
 1    protected   void  btnSSISSearch_Click( object  sender, EventArgs e)
 2          {
 3               // 構造sql語句  作為參數傳遞給數據包
 4               string  sqlParams  =  Jasen.SSIS.Core.SsisToExcel.BuildSql( " dbo.ProvinceSelectedCityInfo " " @provinceId " int .Parse(ddlProvice.SelectedValue));
 5              Jasen.SSIS.Core.SsisToExcel ssis  =   new  Jasen.SSIS.Core.SsisToExcel();
 6               string  rootPath  =  Request.PhysicalApplicationPath;
 7               string  copyFilePath;
 8               // 執行SSIS包的操作  生成EXCEL文件
 9               bool  result  =  ssis.ExportDataBySsis(rootPath, sqlParams,  out  copyFilePath,  " Package.dtsx " " ProviceCityInfoExcel.xls " " ProviceCityInfo " );
10               if  (result  ==   false ){
11                   if  (System.IO.File.Exists(copyFilePath)) System.IO.File.Delete(copyFilePath); 
12              }
13               else
14              {
15                  ssis.DownloadFile( this " ProviceCityInfoClientFile.xls " , copyFilePath,  true );
16              }
17          }   

   

    你肯定會說:“哥,你這個也太簡單了吧?”。就是這么簡單,不就是多寫一個類給你調用就可以了嗎。調用接口,這個你總會吧。不過你得了解各個參數才行。

    首先,我們必須引用2個DLL,Microsoft.SQLServer.ManagedDTS.dll和Microsoft.SqlServer.DTSPipelineWrap.dll(系統自帶的)。先看下我們生成Excel文件數據的步驟,如下:

  ///   <summary>
        
///  導出數據到EXCEL文件中
        
///   </summary>
        
///   <param name="rootPath"></param>
        
///   <param name="sqlParams"> 執行包的傳入參數 </param>
        
///   <param name="copyFile"> 生成的Excel的文件 </param>
        
///   <param name="packageName"> SSIS包名稱 </param>
        
///   <param name="execlFileName"> SSIS EXCEL模板名稱 </param>
        
///   <param name="createdExeclPreName"> 生成的Excel的文件前綴 </param>
        
///   <returns></returns>
         public   bool  ExportDataBySsis( string  rootPath,  string  sqlParams,  out   string  tempExcelName,  string  packageName,  string  execlFileName,  string  createdExeclPreName)
        {
            
// 數據包和EXCEL模板的存儲路徑
             string  path  =   rootPath  +   @" Excel導出\ " ;
            
// 強制生成目錄
             if  ( ! System.IO.Directory.Exists(path)) System.IO.Directory.CreateDirectory(path);
            
// 返回生成的文件名
             string  copyFile  =   this .SaveAndCopyExcel(path, execlFileName, createdExeclPreName);
            tempExcelName 
=  copyFile;
            
// SSIS包路徑
             string  ssisFileName  =  path  +  packageName;
            
// 執行---把數據導入到Excel文件
             return  ExecuteSsisDataToFile(ssisFileName, tempExcelName, sqlParams);
        }

 

   

    代碼注釋如此清楚,想必也不需要再多做解釋了吧,下面就是最最最重要的一步,需要看清楚了----->

 1      private   bool  ExecuteSsisDataToFile( string  ssisFileName,  string  tempExcelName,  string  sqlParams)
 2          {
 3              Application app  =   new  Application();
 4              Package package  =   new  Package();
 5               // 加載SSIS包           
 6              package  =  app.LoadPackage(ssisFileName,  null );
 7               // 獲取 數據庫連接字符串
 8               package.Connections[ " AdoConnection " ].ConnectionString  =  Jasen.SSIS.Common.SystemConst.ConnectionString;
 9               // 目標Excel屬性
10               string  excelDest  =   string .Format( " Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\ " EXCEL  8.0 ;HDR = YES\ " ; " , tempExcelName);
11               package.Connections[ " ExcelConnection " ].ConnectionString  =  excelDest;
12               // 給參數傳值
13              Variables vars  =  package.Variables;
14               string  str  =   vars[ " 用戶::SqlStr " ].Value .ToString();
15              vars[ " 用戶::SqlStr " ].Value  =  sqlParams;
16               // 執行
17               DTSExecResult result  =  package.Execute();
18               if  (result  ==  DTSExecResult.Success){
19                   return   true ;
20              }
21               else {
22                   if  (package.Errors.Count  >   0 ){
23                       // 在log中寫出錯誤列表
24                      StringBuilder sb = new  StringBuilder();
25                       for  ( int  i  =   0 ; i  <  package.Errors.Count; i ++ ){
26                         sb.Append( " Package error: "   +  package.Errors[i].Description  + " ; " );
27                      }
28                       throw   new  Exception(sb.ToString());
29                  }
30                   else {
31                        throw   new  Exception( " SSIS Unknow error " );
32                  }
33                   return   false ;
34              }
35          }

 

      上面標注為紅色的就是最重要的幾個步驟了,相對來說,就是(1)加載包,(2)設置包的數據庫連接串,(3)設置Excel的連接串,(4)設置參數變量,(5)執行操作  

      其次是如何巧妙的將Excel模板復制,使模板可以重復利用(當然也要注意將生成的文件下載到客戶端后,將服務器上生成的Excel臨時文件刪除,你也可以寫自己的算法進行清理不必要的Excel臨時文件),如下代碼所示,方法將復制模板,然后返回生成的臨時文件的路徑,如果需要刪除該文件,System.IO.File.Delete(filePath)就可以刪除文件了:

1    private   string  SaveAndCopyExcel( string  sourcePath,  string  execlFileName,  string  createdExeclPreName)
2          {
3               string  copyFile  =  sourcePath  +  createdExeclPreName  +  DateTime.Now.ToString( " yyyyMMddHHMMss " +   " .xls " ;
4               if  (File.Exists(copyFile)) File.Delete(copyFile);
5              File.Copy(sourcePath  +  execlFileName, copyFile,  true );
6               return  copyFile;
7          }
 
   
     講了這么多,來看下我們點擊后生成的效果,
 
    開始有點效果了,Excel終於可以下載到客戶端了,我們保存該文件。我們是不是想核實一下,我們采用的SSIS方法來實現Excel數據導入是不是正確的,會不會生成錯誤的數據?
    那我們看下下面的一張圖,將它與上面的一張圖比較一下,看下數據是不是一樣的:
 
    發現生成的數據是一模一樣的。我們是將數據導入到服務器上的臨時EXCEL文件中,將文件發送到客戶端肯定是不會出錯的,除了你RP太差以外。RP差,任何事情都可能發生,嘿嘿。
 
(八)總結
    在上面的示例中,由於數據量不是太多,你還感覺不到該方法的優勢(效率高)。但是當數據量很大的時候,你用其他方法還在那里慢慢地等待excel文件生成的時候,該方法早就已經將數據導入到Excel中,並且發送到客戶端了。有時候時間相差幾十秒也是有可能的。數據量越大,效果越明顯.....
    接下來筆者將在另外一篇隨筆中詳細講解SSIS package包的制作過程。這篇主要是SSIS應用篇。
    希望各位能夠在本隨筆中有所收獲。一口氣寫下來,還真不容易,寫文章確實挺鍛煉人的。當然,本文中肯定還有很多不足之處,希望各位多多指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM