2016-02-21 3 views
0

Я написал код, который копирует данные из базы данных Azure в файл Excel. Это можно найти в конце этого вопроса.Копирование большого datatable в excel в ASP.Net

Проблема заключается в том, что для заполнения листа excel требуется навсегда, когда у меня есть 10k строк для одной из таблиц. Очевидно, что это не идеально для Excel, но в этот момент это нужно сделать так. Мне интересно, есть ли более быстрый способ кодировать это. Конечно, создание листа excel является узким местом, потому что C# захватывает набор данных за считанные секунды. Если я перейду в Excel и просмотрю данные, а затем щелкнул правой кнопкой мыши и скопировал их с заголовками и вставил их в листок Excel, он также сделает это за считанные секунды.

enter image description here

Так я могу программно это сделать?

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); 
     } 
+1

Не используйте Excel Interop - это медленно и сложно использовать правильно. Попробуйте Open XML - https://msdn.microsoft.com/en-us/library/office/hh180830(v=office.14).aspx. Но если вы хотите попытаться оптимизировать текущее решение, чем посмотреть на распределение диапазона ячеек. Возьмите диапазон настолько большой, насколько вам нужно, и заполните его по своему усмотрению - это должно немного ускорить работу. –

+0

Я бы порекомендовал вам использовать что-то вроде OpenXML или ClosedXML, также вы могли бы посмотреть на поиск google на 'Convert DataTable to Excel', также вы могли бы написать свой собственный Parser и записать данные в поля, разделенные запятыми, и сохранить файл как .csv – MethodMan

+0

также почему вы делаете это 'foreach (процесс процесса в Process.GetProcessesByName (« Excel »)), когда все, что вам нужно сделать в блоке finally, вызывает вызов« Marshal.ReleaseComObject », поскольку, наконец, всегда бегать .. – MethodMan

ответ

0

Похоже, вы используете автоматизацию Office, которая, как правило, медленна в таких вещах, как это, по моему опыту. Я бы предложил сохранить вывод в виде файла с разделителями (.csv) и с помощью автоматизации открыть этот файл (или файлы) с помощью Excel, а затем сохранить его как электронную таблицу.

+0

Хорошо, это то, о чем я думаю, тоже попробует. – Danrex

+0

Итак, проблема с этим - мне нужно сделать несколько рабочих листов? – Danrex

+0

Не знаю много о автоматизации, но я бы подумал, что вы все равно сделаете это похоже на то, как вы это делаете сейчас. Создайте файл excel, создайте новый рабочий лист, откройте файл csv, скопируйте его на новый рабочий лист и повторите по мере необходимости. –

-1

Я предлагаю вам попробовать использовать какой-либо инструмент ETL, особенно если вы будете это делать время от времени. Например, если вы возьмете Talend, ... вы подключитесь к БД, а схема сама потянется. Возьмите компонент ввода SQL и подключите его к компоненту Excel, и все готово. Возьмите около 5 минут, без одной строки кода

+0

Это приложение SharePoint для одного из пользователей. Поэтому мне нужно, чтобы он был связан с кнопкой загрузки в ASP.Net. – Danrex

+0

не проблема. вы можете экспортировать задание в пакет, который вы можете выполнить из командной строки. – user853710

+0

Или вы берете услуги интеграции и выполняете то же самое в среде MS – user853710

0

Я не уверен, что вы подразумеваете под «навсегда», но для сравнения у меня есть процесс, который пишет таблицу OpenXML в 46124 строк с ~ 500 символами в строке в строке менее 17 секунд. Это генерируется процессом C#, который находится на отдельном сервере на том же хостинге, что и сервер базы данных.

Если запись в CSV является опцией, то это будет решение с наилучшей производительностью. OpenXML даст вам следующую лучшую производительность, я нашел следующую статью, чтобы быть наиболее полезным, когда я пытался собрать мой процесс:

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

Что касается памяти - У вас есть две вещи, которые нужно положить в памяти, ваших входящих данных и вашего исходящего файла. Независимо от того, какой тип файла вы пишете, вы захотите использовать SqlDataReader вместо dataSet. Это означает, что ваши входящие данные будут иметь только одну строку за раз в памяти (вместо всех 10K). При записи файла (CSV или OpenXML), если вы пишете непосредственно на диск (FileStream) вместо памяти (MemoryStream), у вас будет только немного в памяти.

Особенно, если вы используете код, который запущен на вашем веб-сайте, вы не хотите использовать сразу несколько кусков памяти, потому что .NET/IIS не очень хорошо справляется с этим.

Смежные вопросы