本文目錄:
(一)背景
   (二)數據庫數據導入到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)。當數據量大時,肯定效率還是有影響的。
 
          
 
          {
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組件。一般,對於操作能夠基本滿足,但對於數據量大時可能會慢點。下面的代碼,本人在原有基礎上稍微修改了下,如下所示:
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自帶的屬性。如下:
 
          
 
          {
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.(主要是為了簡單,便於講解)
其中存儲過程如下:
(
@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包的時候很容易混淆,建議拿個本子把需要的內容寫好。
 
         這一步是最主要的過程,當然,也是很容易出錯的一步。 筆者會另外詳細介紹制作Package包的過程,本文將直接將生成的包放到VS項目中進行運用。
利用SQL Server 2005數據庫自帶的SQL Server Business Intelligence Development Studio(SQL Server商業智能開發平台),最終生成的項目如下圖所示:
 
         
 
         
 
         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文件數據的步驟,如下:
/// 導出數據到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);
}
代碼注釋如此清楚,想必也不需要再多做解釋了吧,下面就是最最最重要的一步,需要看清楚了----->
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)就可以刪除文件了:
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 }
 
         
 
         