如何加快将DataTable转储到Excel工作表?

我有以下例程将DataTable转储到Excel工作表中。

private void RenderDataTableOnXlSheet(DataTable dt, Excel.Worksheet xlWk, string [] columnNames, string [] fieldNames) { // render the column names (eg headers) for (int i = 0; i < columnNames.Length; i++) xlWk.Cells[1, i + 1] = columnNames[i]; // render the data for (int i = 0; i < fieldNames.Length; i++) { for (int j = 0; j < dt.Rows.Count; j++) { xlWk.Cells[j + 2, i + 1] = dt.Rows[j][fieldNames[i]].ToString(); } } } 

无论出于何种原因,在我相对较新的个人电脑上,倾倒25列和400行的DataTable需要大约10-15秒。 需要更长的testing者的机器。

有什么我可以做的,以加快这个代码? 或者互操作本来就很慢?

解决scheme:基于海伦托米克的build议,我已经修改了方法,它现在应该适用于几种常见的数据types(int32,double,datetime,string)。 随意扩展它。 处理我的数据集的速度从15秒到不足1秒。

  private void RenderDataTableOnXlSheet(DataTable dt, Excel.Worksheet xlWk, string [] columnNames, string [] fieldNames) { Excel.Range rngExcel = null; Excel.Range headerRange = null; try { // render the column names (eg headers) for (int i = 0; i < columnNames.Length; i++) xlWk.Cells[1, i + 1] = columnNames[i]; // for each column, create an array and set the array // to the excel range for that column. for (int i = 0; i < fieldNames.Length; i++) { string[,] clnDataString = new string[dt.Rows.Count, 1]; int[,] clnDataInt = new int[dt.Rows.Count, 1]; double[,] clnDataDouble = new double[dt.Rows.Count, 1]; string columnLetter = char.ConvertFromUtf32("A".ToCharArray()[0] + i); rngExcel = xlWk.get_Range(columnLetter + "2", Missing.Value); rngExcel = rngExcel.get_Resize(dt.Rows.Count, 1); string dataTypeName = dt.Columns[fieldNames[i]].DataType.Name; for (int j = 0; j < dt.Rows.Count; j++) { if (fieldNames[i].Length > 0) { switch (dataTypeName) { case "Int32": clnDataInt[j, 0] = Convert.ToInt32(dt.Rows[j][fieldNames[i]]); break; case "Double": clnDataDouble[j, 0] = Convert.ToDouble(dt.Rows[j][fieldNames[i]]); break; case "DateTime": if (fieldNames[i].ToLower().Contains("time")) clnDataString[j, 0] = Convert.ToDateTime(dt.Rows[j][fieldNames[i]]).ToShortTimeString(); else if (fieldNames[i].ToLower().Contains("date")) clnDataString[j, 0] = Convert.ToDateTime(dt.Rows[j][fieldNames[i]]).ToShortDateString(); else clnDataString[j, 0] = Convert.ToDateTime(dt.Rows[j][fieldNames[i]]).ToString(); break; default: clnDataString[j, 0] = dt.Rows[j][fieldNames[i]].ToString(); break; } } else clnDataString[j, 0] = string.Empty; } // set values in the sheet wholesale. if (dataTypeName == "Int32") rngExcel.set_Value(Missing.Value, clnDataInt); else if (dataTypeName == "Double") rngExcel.set_Value(Missing.Value, clnDataDouble); else rngExcel.set_Value(Missing.Value, clnDataString); } // figure out the letter of the last column (supports 1 letter column names) string lastColumn = char.ConvertFromUtf32("A".ToCharArray()[0] + columnNames.Length - 1); // make the header range bold headerRange = xlWk.get_Range("A1", lastColumn + "1"); headerRange.Font.Bold = true; // autofit for better view xlWk.Columns.AutoFit(); } finally { ReleaseObject(headerRange); ReleaseObject(rngExcel); } } private void ReleaseObject(object obj) { try { System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); obj = null; } catch { obj = null; } finally { GC.Collect(); } } 

不要一个接一个地设置单元值,而是一批一批地完成。

步骤1:将DataTable中的数据转移到具有相同维度的数组中。

第2步。定义跨越适当范围的Excel范围对象。

第3步。将Range.Value设置为数组。

这将会快很多,因为跨Interop边界(一个获取Range对象,一个设置其值)而不是每个单元两个(获取单元格,设置值),总共需要两个调用。

有一些示例代码在MSDN知识库文章302096 。

互操作本来就很慢。 每次通话都有很大的开销。 为了加快速度,可以尝试在一个赋值语句中将一个数据对象数组写回到一个单元格区域。

或者,如果这是一个严重的问题,请尝试使用可通过XLL界面使用托pipe代码读取/写入数据的托pipe代码Excel扩展之一。 (Addin Express,Managed XLL等)

如果您有logging集,写入Excel的最快方法是CopyFromRecordset。

你有具体的要求去COM自动化路线? 如果没有,你还有其他的select。

  1. 使用OLEDB提供程序创build/写入Excel文件
    http://support.microsoft.com/kb/316934

  2. 使用第三方库来写入Excel。 根据您的许可要求,有几个选项。 更新:一个好的免费图书馆是NPOI http://npoi.codeplex.com/

  3. 将数据写入一个csv文件,并将其加载到Excel中

  4. 把数据写成可以加载到Excel中的XML。

  5. 使用Open XML SDK
    http://www.microsoft.com/downloads/details.aspx?familyid=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en

你可以创build一个Excel加载项,使用VBA代码来完成所有数据库的重要工作。 从.NET开始,您只需要实例化Excel,添加插件,然后调用Excel VBA例程,并将任何parameter passing给它,以执行SQL语句。

我同意查尔斯的观点 Interop真的很慢。 但是试试这个:

 private void RenderDataTableOnXlSheet(DataTable dt, Excel.Worksheet xlWk, string [] columnNames, string [] fieldNames) { // render the column names (eg headers) int columnLength = columnNames.Length; for (int i = 0; i < columnLength; i++) xlWk.Cells[1, i + 1] = columnNames[i]; // render the data int fieldLength = fieldNames.Length; int rowCount = dt.Rows.Count; for (int j = 0; j < rowCount; j++) { for (int i = 0; i < fieldLength; i++) { xlWk.Cells[j + 2, i + 1] = dt.Rows[j][fieldNames[i]].ToString(); } } } 

HTH