c#-如何提高foreach循环的性能

c#-如何提高foreach循环的性能,c#,foreach,C#,Foreach,我有一个c#程序,可以将数据导出到excel文件。它调用sql server中的存储过程,并将结果返回到excel文件(基于我选择的过滤器) 现在,我能够成功地导出它,但如果我试图从一个2000万条记录表中提取更多的数据(20000条记录左右),那就太长了。我在代码中添加了秒表,发现foreach循环是罪魁祸首 这是我的密码: private void ExportExcel(SqlDataReader dr) { try { Da

我有一个c#程序,可以将数据导出到excel文件。它调用sql server中的存储过程,并将结果返回到excel文件(基于我选择的过滤器)

现在,我能够成功地导出它,但如果我试图从一个2000万条记录表中提取更多的数据(20000条记录左右),那就太长了。我在代码中添加了秒表,发现foreach循环是罪魁祸首

这是我的密码:

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();
                            }
                        }
                    }
                }

有人能就如何提高导出到excel所需的性能提出一些建议吗?

没有明显的方法可以改进这一点。如果循环需要时间(而不是sql过程),那么大部分工作必须由excel库完成。您可以查看不同的excel库,如果幸运的话,其中一个库可以允许并行构建行,或者更快。否则,您只能在循环中对自己的代码进行小的优化。您可能会对日期-时间代码进行一些改进,但如果这会产生很大的不同,我会感到惊讶


您需要excel工作表还是csv(可以通过excel打开)呢?如果csv可以工作,那么可以直接从sql导出或自己并行构建行,而无需依赖库。

没有明显的方法来改进这一点。如果循环需要时间(而不是sql过程),那么大部分工作必须由excel库完成。您可以查看不同的excel库,如果幸运的话,其中一个库可以允许并行构建行,或者更快。否则,您只能在循环中对自己的代码进行小的优化。您可能会对日期-时间代码进行一些改进,但如果这会产生很大的不同,我会感到惊讶


您需要excel工作表还是csv(可以通过excel打开)呢?如果csv可以工作,那么可以直接从sql导出或自己并行构建行,而无需依赖库。

如何通过COM互操作访问Excel

如果是这样,您将希望避免单独设置每个单元格的值,因为这样做确实非常慢。尝试将数组分配给尽可能大的范围


例如,由于您似乎无条件地迭代每一行,因此您至少可以尝试构建表示一列的数组/向量,并将其分配到该列中的适当范围。

如何通过COM互操作访问Excel

如果是这样,您将希望避免单独设置每个单元格的值,因为这样做确实非常慢。尝试将数组分配给尽可能大的范围



例如,由于您似乎无条件地迭代每一行,因此您至少可以尝试构建表示一列的数组/向量,并将其分配到该列中的适当范围。

您使用哪个框架来创建和写入工作表?在将数据读取到系统之前,您应该先过滤掉数据,然后再使用以下条件(c.ColumnMapping!=MappingType.Hidden)和(c.DataType==typeof(DateTime)应该设置为db级别,这样您将有更少的数据需要处理,并且不需要此条件complexity@Magnus这是一个旧的应用程序,它的目标是2.0 Net framework。我的意思是什么是
excel
?它是一个外部库还是自动化或其他?@Guru是有意义的,但每个(dt.Columns中的DataColumn c)也取决于这些条件。那么这仍然可能吗?您使用哪个框架来创建和写入工作表?在将数据读取到系统之前,您应该先过滤掉数据,例如(c.ColumnMapping!=MappingType.Hidden)和(c.DataType==typeof(DateTime)应该设置为db级别,这样您将有更少的数据需要处理,并且不需要此条件complexity@Magnus这是一个旧的应用程序,它的目标是2.0 Net framework。我的意思是什么是
excel
?它是一个外部库还是自动化或其他?@Guru是有意义的,但每个(dt.Columns中的数据列c)也取决于这些条件。那么这仍然可能吗?CSV也可以。当我导出和过滤日期(比如3年前到今天)时,事情是这样的而且只有大约20k条记录,它甚至不会加载完成。虽然我认为改进循环不会对性能产生很大的改善。即使到目前为止,仅导出一个月内包含的数据少于2000条,需要4-5分钟。我查阅了外部库,如EPPlus,它有助于更快地导出数据,但问题是Only适用于net framework 3.5及更高版本,而我的应用程序使用的是2.0,因此这将是一个巨大的问题。您可以在不使用任何外部库的情况下创建csv。但我认为这不会是一个巨大的改进。在我的场景中,我必须寻求一个巨大的改进。如果该循环占用了大部分时间,那么您可以自己使用字符串来编写csvols可能会是一个很大的改进CSV也会这样做。当我导出和过滤日期时(比如说3年前到今天)而且只有大约20k条记录,它甚至不会加载完成。虽然我认为改进循环不会对性能产生很大的改善。即使到目前为止,仅导出一个月内包含的数据少于2000条,需要4-5分钟。我查阅了外部库,如EPPlus,它有助于更快地导出数据,但问题是Only适用于net framework 3.5及更高版本,而我的应用程序使用的是2.0,因此这将是一个巨大的问题。您可以在不使用任何外部库的情况下创建csv。但我认为这不会是一个巨大的改进。在我的场景中,我必须寻求一个巨大的改进。如果这个循环占用了大部分时间,那么您可以自己使用