简体   繁体   English

CodeGo.net>如何提高foreach循环的性能

[英]c# - How to increase performance of foreach loop

I have this c# program where I am able to export data to an excel file. 我有这个C#程序,可以在其中将数据导出到Excel文件。 It calls the stored procedure in sql server and returns the results to the excel file (based on the filter I choose). 它在sql server中调用存储过程,并将结果返回到excel文件(基于我选择的过滤器)。

Right now, I am able to export it successfully but if I try to extract way more data ( 20,000 records or so) from a 20 million record table, it is taking way way too long. 现在,我能够成功导出它,但是如果我尝试从2000万个记录表中提取更多数据(约20,000个记录),则它的时间太长了。 I added a stopwatch to the code and found that the foreach loop is the culprit here. 我在代码中添加了一个秒表,发现foreach循环是这里的罪魁祸首。

Here is my code: 这是我的代码:

private void ExportExcel(SqlDataReader dr)
    {
        try
        {
            DataTable dt = new DataTable();
            dt.Load(dr);
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "Microsoft Office Excel Workbook (*.xls)|*.xls|All Files (*.*)|*.*";
            saveFileDialog1.FilterIndex = 1;
            saveFileDialog1.RestoreDirectory = true;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                // Create an Excel object and add workbook...
                Excel.ApplicationClass excel = new Excel.ApplicationClass();
                Excel.Workbook workbook = excel.Application.Workbooks.Add(true); // true for object template???


               // var watch = System.Diagnostics.Stopwatch.StartNew();
                // Add column headings...
                int iCol = 0;
                int iVisibleColumnCount = 0;
                foreach (DataColumn c in dt.Columns)
                {
                    iCol++;
                    // counting visible columns
                    if (c.ColumnMapping != MappingType.Hidden)
                        iVisibleColumnCount++;
                    else    // hide the columns in excel if the column is hide in datatable
                    {
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.Hidden = true;
                        continue;
                    }
                    // Set column header text to bold
                    ((Excel.Range)excel.Cells[1, iCol]).Font.Bold = true;
                    excel.Cells[1, iCol] = c.ColumnName;

                    if (c.DataType == typeof(System.String))
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = "@";
                    else if (c.DataType == typeof(System.Int16)
                        || c.DataType == typeof(System.Int32)
                        || c.DataType == typeof(System.Int64))
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = "#,##0";
                    else if (c.DataType == typeof(System.TimeSpan))
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = @"[$-409]hh:mm:ss AM/PM;@";
                    else if (c.DataType == typeof(System.DateTime))
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = "yyyy-mm-dd hh:mm:ss";
                    else if (c.DataType == typeof(System.Decimal))
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = @"#,##0.00_);[Red](#,##0.00)";
                    else
                        ((Excel.Range)excel.Cells[1, iCol]).EntireColumn.NumberFormat = "General";
                }
                // for each row of data...
                int iRow = 0;
                foreach (DataRow r in dt.Rows)
                {
                    iRow++;

                    // add each row's cell data...
                    iCol = 0;
                    foreach (DataColumn c in dt.Columns)
                    {
                        iCol++;
                        if (c.ColumnMapping != MappingType.Hidden)
                        {
                            if (c.DataType == typeof(DateTime))
                            {
                                DateTime date1 = (DateTime)r[c.ColumnName];
                                string DateTime = date1.ToString();

                                if (DateTime.Contains("AM"))
                                {
                                    excel.Cells[iRow + 1, iCol] = date1.ToString("yyyy-MM-dd hh:mm:ss") + " AM";
                                }
                                else   {
                                    excel.Cells[iRow + 1, iCol] = date1.ToString("yyyy-MM-dd hh:mm:ss") + " PM";
                                }


                            }
                            else {
                                excel.Cells[iRow + 1, iCol] = r[c.ColumnName].ToString();
                            }
                        }
                    }
                }

Can anyone give some suggestion on how to improve the performance it takes to export to excel? 谁能提出一些建议,以改善导出至Excel所需的性能?

How are you accessing Excel - via COM interop? 您如何通过COM互操作访问Excel?

If so, you'll want to avoid setting each cell's value individually, because that is very slow indeed. 如果是这样,您将避免避免单独设置每个单元格的值,因为这确实非常慢。 Try to assign arrays to ranges as large as you possibly can. 尝试将数组分配给尽可能大的范围。

For example, since you seem to iterate over each row unconditionally, it looks like you could at least try build an array/vector representing one column, and assigning it to an appropriate range in that column. 例如,由于您似乎无条件地遍历了每一行,因此看起来您至少可以尝试构建代表一个列的数组/向量,并将其分配给该列中的适当范围。

There's no obvious way to improve this. 没有明显的方法可以改善这一点。 If the loop is taking the time (rather than the sql procedure) then the majority of the work must be being done by the excel library. 如果循环花费时间(而不是sql过程),则大部分工作必须由excel库完成。 You could look into different libraries for excel, and if you're lucky one might allow building rows in parallel, or just be faster. 您可以针对excel寻找不同的库,如果幸运的话,您可以允许并行构建行,或者只是更快地构建行。 Otherwise you're limited to making small optimisations on your own code inside the loop. 否则,您将只能在循环内对自己的代码进行小的优化。 You could probably improve that date time code a bit, but I'd be surprised if it would make much difference. 您可能可以稍微改善一下日期时间代码,但是如果它会产生很大的变化,我会感到惊讶。

Do you need an excel sheet or would a csv (which can be opened by excel) do? 您需要Excel工作表还是CSV(可以由Excel打开)? If a csv would work then it might be possible to export from sql directly or build the rows in parallel yourself without relying on libraries. 如果csv可以工作,则可以直接从sql导出,也可以自己建立并行行,而无需依赖库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM