English 中文(简体)
如何加快将数据输入Excel工作表格?
原标题:How to speed up dumping a DataTable into an Excel worksheet?

我有以下例行做法,将数据输入Excel工作表格。

    private void RenderDataTableOnXlSheet(DataTable dt, Excel.Worksheet xlWk, 
                                    string [] columnNames, string [] fieldNames)
    {
        // render the column names (e.g. 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行,在我比较现代的PC上大约需要10-15秒钟。 服更长的检测器机。

我是否可以加快这一法典? 或相互协作只是内在的缓慢吗?

消费:根据海伦·托米克的建议,我修改了这一方法,现在该方法应当用于几种共同数据类型(int32,双倍,日间,扼杀)。 感到可以自由延伸。 我的数据集处理速度从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 (e.g. 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 将数据从你的可数据转换成一个具有相同层面的阵列。

步骤 2. 定义 范围标的涵盖范围适当。

步骤 3. 确定范围。 阵列的价值。

这将更快,因为你总共有两条跨越Interop边界的通话(一条是接收地标,一是确定其价值),而不是每个囚室(植被单位,固定价值)。

上有一些样本代码。MSDN KB 第302096条。

问题回答

Interop is inherently very slow. There is a large overhead associated with each call. To speed it up try writing back an object array of data to a range of cells in one assignment statement.

或者,如果这是一个严重问题,则利用经管理的《法典》延伸部分,通过“XLL”接口使用管理代码读/篡改数据。 (Adin Express, Executiveed XLL等)

如果你有记录,写到Excel的最快方式是复印件。

您是否有一个具体要求,要走COM自动化线路? 如果没有,你还有其他几个选择。

  1. 利用欧LEDB提供商创建/加入Excel文档

  2. Use a third party library to write to Excel. Depending on your licensing requirements there are a few options. Update: A good free library is NPOI http://npoi.codeplex.com/

  3. 将数据抄录到单上,并装入Excel

  4. 将数据作为可装入Excel的XML书写。

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

Interop has the fastest method called CopyFromRecordset but ADODB library has to be used

显然,最快的方式/方法,我已经尝试过几个方法。 也许不易使用,但速度令人吃惊:

https://learn.microsoft.com/en-us/office/vba/api/excel.range.copy fromrecordset

a 简短样本:

using ADODB;
using Microsoft.Office.Interop;

//--- datatable --- already exists
DataTable dt_data = new DataTable();
//--- or your dt code is here ..........


//--- mine has 3 columns ------

//--- code to populate ADO rs with DataTable data --- nothing special
//--- create empty rs .....
ADODB.Recordset rs = new ADODB.Recordset();
rs.CursorType = CursorTypeEnum.adOpenKeyset;
rs.CursorLocation = CursorLocationEnum.adUseClient;
rs.LockType = LockTypeEnum.adLockOptimistic;
rs.Fields.Append("employee_id",DataTypeEnum.adBSTR,255,FieldAttributeEnum.adFldIsNullable);
rs.Fields.Append("full_name", DataTypeEnum.adBSTR, 255, FieldAttributeEnum.adFldIsNullable);
rs.Fields.Append("start_date", DataTypeEnum.adBSTR, 10, FieldAttributeEnum.adFldIsNullable);
rs.Open();

//--- populate ADO rs with DataTable data ----    
for (int i = 0; i < dt_data.Rows.Count; i++)
{
    rs.AddNew();
    rs.Fields["employee_id"].Value = dt_data.Rows[i]["employee_id"].ToString();
    rs.Fields["full_name"].Value = dt_data.Rows[i]["full_name"].ToString();
    //--- if date is empty......
    if (dt_data.Rows[i]["start_date"].ToString().Length > 0)
    {
        rs.Fields["start_date"].Value = dt_data.Rows[i]["start_date"].ToString();
    }
    rs.Update();
}

Microsoft.Office.Interop.Excel.Application xlexcel;
Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
xlexcel = new Microsoft.Office.Interop.Excel.Application();
xlexcel.Visible = true;


xlWorkBook = xlexcel.Workbooks.Add(misValue);
xlWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

//--- populate columns from rs --
for (int i = 0; i < rs.Fields.Count; i++)
{
    xlWorkSheet.Cells[1, i + 1] = rs.Fields[i].Name.ToString();
};

//----- .CopyFromRecordset method -- (rs object, MaxRows, MaxColumns) --- in this case 3 columns but it can 1,2,3 etc ------
xlWorkSheet.Cells[2, 1].CopyFromRecordset(CloneFilteredRecordset(rs), rs.RecordCount, 3);

您可以增设一个Excel公司,由VBA公司编码完成全部重提。 从www.NET来看,你们都需要做的是即时的Excel,加上“Excel VBA”例行公事,通过执行你的发言所需的任何参数。

我同意查尔斯。 互操作实际上进展缓慢。 但试图这样做:

private void RenderDataTableOnXlSheet(DataTable dt, Excel.Worksheet xlWk, 
                                    string [] columnNames, string [] fieldNames)
{
    // render the column names (e.g. 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





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签