【问题标题】:Copying large datatable into excel in ASP.Net在 ASP.Net 中将大型数据表复制到 excel 中
【发布时间】:2016-02-21 21:51:59
【问题描述】:

我编写了一些代码,将 Azure 数据库中的数据复制到 Excel 文件中。这可以在这个问题的末尾找到。

问题是当我为其中一个表有 10k 行时,填充 Excel 工作表需要很长时间。显然,这对于 Excel 来说并不理想,但此时必须以这种方式完成。我想知道是否有更快的方法来编码。 当然,创建 excel 表是瓶颈,因为 C# 可以在几秒钟内获取数据集。如果我进入 Excel 并查看数据,然后右键单击并复制标题并将其粘贴到 Excel 表中,它也会在几秒钟内完成。

那么我可以以编程方式执行此操作吗?

private void createExcelFile()
        {
            string fileName = "FvGReport.xlsx";
            string filePath = HttpContext.Current.Request.MapPath("~/App_Data/" + fileName); //check www.dotnetperls.com/mappath
            string sqlQuery = "";
            List<string> sheetNames = new List<string>();

            foreach (ListItem item in ddlSummary_Supplier.Items)
            {
                string sqlSummary = "SELECT * FROM FvGSummaryAll WHERE Supplier_Code = '" + item.Text + "'; ";
                sqlQuery = sqlQuery + sqlSummary;
                sheetNames.Add("Summary " + item.Text);

                string sqlPaymentsSummary = "SELECT * FROM FvGSummaryPayment WHERE Supplier_Code = '" + item.Text + "'; ";
                sqlQuery = sqlQuery + sqlPaymentsSummary;
                sheetNames.Add("PaymentSummary " + item.Text);
            }

            DataSet dataSet = new DataSet();
            //string sqlQuery = @"SELECT * FROM FvGData WHERE Supplier_Code = 'SFF Pacific'; SELECT * FROM FvGSummaryPayment";


            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(sqlQuery, connection);
                adapter.Fill(dataSet);
            }

            //this reference conflicts with System.Data as both have DataTable. So defining it here. 
            Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook excelWorkBook = null;
            Microsoft.Office.Interop.Excel.Worksheet excelWorkSheet = null;
            ExcelApp.Visible = true;
            excelWorkBook = ExcelApp.Workbooks.Add(Microsoft.Office.Interop.Excel.XlWBATemplate.xlWBATWorksheet);


            //excel rows start at 1 not 0
            try
            {
                for (int i = 1; i < dataSet.Tables.Count; i++)
                {
                    excelWorkBook.Worksheets.Add();  //Adds new sheet in Excel WorkBook
                }

                for (int i = 0; i < dataSet.Tables.Count; i++)
                {
                    int dsRow = 1;
                    excelWorkSheet = excelWorkBook.Worksheets[i + 1];

                    //Writing Columns Name in Excel Sheet
                    for (int col = 1; col < dataSet.Tables[i].Columns.Count; col++)
                    {
                        excelWorkSheet.Cells[dsRow, col] = dataSet.Tables[i].Columns[col - 1].ColumnName;
                    }
                    dsRow++;

                    for (int xlRow = 0; xlRow < dataSet.Tables[i].Rows.Count; xlRow++)
                    {
                        //Excel row and col positions for writing row = 1, col = 1
                        for (int col = 1; col < dataSet.Tables[i].Columns.Count; col++)
                        {
                            excelWorkSheet.Cells[dsRow, col] = dataSet.Tables[i].Rows[xlRow][col - 1].ToString();                           
                        }
                        dsRow++;
                    }

                    excelWorkSheet.Name = sheetNames[i]; //Renaming ExcelSheets
                }

                excelWorkBook.SaveAs(filePath);
                excelWorkBook.Close();
                ExcelApp.Quit();
                Marshal.ReleaseComObject(excelWorkSheet);
                Marshal.ReleaseComObject(excelWorkBook);
                Marshal.ReleaseComObject(ExcelApp);
            }
            catch (Exception ex)
            {
                lblNoData.Text = ex.ToString();
            }
            finally
            {
                foreach (Process process in Process.GetProcessesByName("Excel"))
                {
                    process.Kill();
                }
            }

            downloadExcel(filePath, fileName);
        }

【问题讨论】:

  • 不要使用 Excel 互操作 - 它速度慢且难以正确使用。试试 Open XML - msdn.microsoft.com/en-us/library/office/…。但是,如果您想尝试优化当前解决方案而不是查看单元格范围分配。抓住一个尽可能大的范围并根据需要填充它 - 这应该会加快速度。
  • 我建议你看看使用类似OpenXML or ClosedXML 的东西,你也可以看看在Convert DataTable to Excel 上进行谷歌搜索,你也可以编写自己的解析器并将数据写入逗号分隔的字段并将文件另存为 .csv
  • 还有你为什么要这样做foreach (Process process in Process.GetProcessesByName("Excel")),而在 finally 块中你所要做的就是调用Marshal.ReleaseComObject,因为 finally 将始终运行..

标签: c# asp.net sql-server excel


【解决方案1】:

根据我的经验,您似乎正在使用 Office 自动化,在这样的事情上通常很慢。我建议将输出保存为分隔文件 (.csv),并使用自动化功能使用 Excel 打开该文件(或多个文件),然后将其保存为电子表格。

【讨论】:

  • 好吧,我也想试试。
  • 所以问题是我需要制作多个工作表?
  • 我对自动化知之甚少,但我认为您仍然会像现在一样这样做。创建一个 excel 文件,创建一个新工作表,打开 csv 文件,将其复制到新工作表并根据需要重复。
【解决方案2】:

我建议你尝试使用一些 ETL 工具,特别是如果你会不时地这样做。例如,如果您使用 Talend,......您连接到数据库,架构将自行提取。获取一个 SQL 输入组件并将其连接到一个 Excel 组件,您就完成了。大约需要5分钟,没有一行代码

【讨论】:

  • 这是针对其中一位用户的 SharePoint 应用程序。所以我需要它链接到 ASP.Net 中的下载按钮。
  • 不是问题。您可以将作业导出到可以从命令行执行的包中。
  • 或者您采用集成服务并在 MS 环境中做同样的事情
【解决方案3】:

我不确定你所说的“永远”是什么意思,但为了比较,我有一个过程可以在不到 17 秒的时间内编写一个包含 46,124 行且每行约 500 个字符的 OpenXML 电子表格。这是由 C# 进程生成的,该进程位于与数据库服务器相同的托管设施的单独服务器上。

如果写入 CSV 是一种选择,那么这将是性能最佳的解决方案。 OpenXML 将为您提供下一个最佳性能,当我尝试整理我的流程时,我发现以下文章最有帮助:

Read-and-Write-Microsoft-Excel-with-Open-XML-SDK

关于内存——您需要将两件事放入内存,即传入数据和传出文件。无论您编写什么类型的文件,您都需要使用SqlDataReader 而不是dataSet。这意味着您的传入数据在内存中一次只有一行(而不是全部 10K)。在写入文件(CSV 或 OpenXML)时,如果您直接写入磁盘 (FileStream) 而不是内存 (MemoryStream),您的内存中只会有那么一点点。

特别是如果您正在运行在您的网站中运行的代码,您不希望一次用完大量内存,因为 .NET/IIS 不能很好地处理这些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多