【问题标题】:What is the fastest way to export dataGridView rows to Excel or into an SQL Server database将 dataGridView 行导出到 Excel 或 SQL Server 数据库的最快方法是什么
【发布时间】:2012-07-29 08:34:42
【问题描述】:

在不使用 Microsoft Office 互操作的情况下,将 460328 - 800328 范围内的 DataGridView 行导出到 Excel 或 SQL Server 数据库表中的最快方法是什么,因为互操作非常慢且占用系统资源很重?

【问题讨论】:

  • 最快的方式?或者,第一方式?
  • Excel 和 SQL 是非常不同的目标,需要不同的解决方案。请你能更具体一点,例如CSV 是否足以用于 Excel?然后可以将数据批量导入 SQL Server 吗?这是一次性的练习还是需要反复练习。
  • 查看这个stackoverflow.com/questions/10248361/… 或这个stackoverflow.com/questions/5022531/… 获取sql。对于大型数据集,逐行插入会很慢。
  • 我不得不承认我从来不需要在stackoverflow.com/questions/10248361/… 中使用 SQLBulkCopy,这可能是一个比我在下面发布的更好的解决方案,性能方面。不过想看看数字。
  • @GrayFox374 它快了很多 - 没有那么多数字,因为它快得多,测量起来不是问题。至少数量级。但是你必须做很多插入才能需要它。我已经将使用它需要花费数十分钟的过程减少到几秒钟。

标签: c# datagridview streamwriter


【解决方案1】:

一种选择是将数据写入 CSV 文件而不是 Excel 文件。 Excel 之后读取它没有问题。

如果您不熟悉,在 CSV(即逗号分隔)文件中,字段由逗号分隔,行由换行符分隔(\n\r\n)。

类似的东西(可能无法编译!):

private void WriteData() {
    using (var file = System.IO.StreamWriter(@"C:\Path\To\File.csv")) {
        foreach (var row in dataGrid.Rows) {
             foreach (var cell in row.Cells) {
                 // Note that if some cells contain commas, 
                 // you'd need to wrap them in quotes.
                 file.Write(cell.Value).Write(",");
             }
        }
        file.Write("\n");
    }
}

为了获得更快的性能,最好将几百(或几千)行收集到一个字符串中,然后将其写入文件,而不是逐个单元格地写入。

【讨论】:

【解决方案2】:

对于导出到 Excel,如果您不使用基于 XML 的 2007 或 2010,Interop 几乎是唯一的方法。不过也没有名气那么差。我将列出一些解决方案。

1 到 Excel

首先将 Microsoft.Office.Interop.Excel 组件引用添加到您的项目。这应该在 Project -> Add Reference 中的 .NET 选项卡下。 将 using 语句添加到您的表单中:

使用 Excel = Microsoft.Office.Interop.Excel;

添加一个按钮控件,并将这段代码添加到它的主体中:

    private void btnExport_Click(object sender, EventArgs e)
    {

        Excel.Application app = new Excel.Application();
        app.Visible = true;
        Excel.Workbook wb = app.Workbooks.Add(1);
        Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
        // changing the name of active sheet
        ws.Name = "Exported from gridview";

        ws.Rows.HorizontalAlignment = HorizontalAlignment.Center;
        // storing header part in Excel
        for (int i = 1; i < dataGridView1.Columns.Count + 1; i++)
        {
            ws.Cells[1, i] = dataGridView1.Columns[i - 1].HeaderText;
        }


        // storing Each row and column value to excel sheet
        for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
        {
            for (int j = 0; j < dataGridView1.Columns.Count; j++)
            {
                ws.Cells[i + 2, j + 1] = dataGridView1.Rows[i].Cells[j].Value.ToString();
            }
        }

        // sizing the columns
        ws.Cells.EntireColumn.AutoFit();

        // save the application
        wb.SaveAs("c:\\output.xls",Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive , Type.Missing, Type.Missing, Type.Missing, Type.Missing);

        // Exit from the application
       app.Quit();
    }
}

2 - 到 SQL Server

这不需要互操作。为了便于使用,请将您的 List 对象传递给执行插入的事件。如果您将表格设置为与您的网格视图列相对应,这很容易。在这里,我使用了存储过程。

    private void btnToSQL_Click(object sender, EventArgs e)
    {
        string connStr = @"Data Source=(local)\sqlexpress;Initial Catalog=rTALIS;Integrated Security=True";
        var cn = new SqlConnection(connStr);
        var cm = new SqlCommand("exec usp_InsertRecord", cn);
        cm.CommandType = System.Data.CommandType.StoredProcedure;
        try
        {
            cn.Open();
            foreach (Row r in rows)
            {
                cm.Parameters.Clear();
                cm.Parameters.AddWithValue("@Number1", r.Number1);
                cm.Parameters.AddWithValue("@Number2", r.Number2);
                cm.Parameters.AddWithValue("@Number3", r.Number3);
                cm.Parameters.AddWithValue("@Number4", r.Number4);
                cm.Parameters.AddWithValue("@Number5", r.Number5);
                cm.Parameters.AddWithValue("@Number6", r.Number6);
                cm.Parameters.AddWithValue("@Number7", r.Number7);
                cm.Parameters.AddWithValue("@Date1", r.Date1);
                cm.ExecuteNonQuery();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            cn.Close();
        }
    }

如果我需要为你调整这个,请告诉我。在原始示例中,我有 List rows = new List();在 form_Load 方法中声明。这适用于该解决方案,但现在它的范围太有限了。我已经将它上移/移出到类中,以便可以在表单上的任何位置调用 in(特别是 btnToSQL_Click)。我在下面评论了它:

    List<Row> rows = new List<Row>();

    private void Form1_Load(object sender, EventArgs e)
    {
        //var rows = new List<Row>();  //limited scope
        var sr = new StreamReader(@"C:\so_test.txt");
        while (!sr.EndOfStream)
        {
            string s = sr.ReadLine();
            if (!String.IsNullOrEmpty(s.Trim()))
            {
                rows.Add(new Row(s));
            }
        }
        sr.Close();
        dataGridView1.DataSource = rows;
    }

【讨论】:

  • 请再次阅读问题。 OP 写道:with out using Microsoft office interop as interop is quite slow and heavy
【解决方案3】:

对于转移到 Excel,这是我发现的最快的方法(尽管它确实使用 Office InterOp)。循环遍历 DataGridView 中的每个单元格并将其分配给对象数组。然后将整个数组分配给 Excel 范围。这比单独为每个 Excel 单元格赋值要快得多,因为它只调用一次 InterOp。请原谅 VB:

Sub Export()
    Dim xlApp As New Excel.Application
    Dim wb As Excel.Workbook = xlApp.Workbooks.Add
    Dim ws As Excel.Worksheet = wb.Worksheets(1)
    Dim dgv as DataGridView = MyDataGridView

    Dim ExportArray(dgv.Rows.Count, dgv.Columns.Count - 1) As Object
    Dim j, i As Integer

    For j = 0 To dgv.Columns.Count - 1
        ExportArray(0, j) = dgv.Columns(j).Name
        For i = 1 To dgv.Rows.Count
            ExportArray(i, j) = dgv(j, i - 1).Value
        Next
    Next

    Dim col As String = ColNumtoLetter(j)
    ws.Range("A1:" & col & i).Value = ExportArray
End Sub

Private Function ColNumtoLetter(ByVal iCol As Integer) As String
    Dim Result As String = ""

    Dim iAlpha As Integer = Int(iCol / 26.001)
    Dim iRemainder As Integer = iCol - (iAlpha * 26)

    If iAlpha > 0 Then
        Result = Chr(iAlpha + 64)
    End If
    If iRemainder > 0 Then
        Result = Result & Chr(iRemainder + 64)
    End If

    Return Result
End Function

第二种方法只是将最终的列号转换为相应的 Excel 列名。

有关详细信息,请参阅“Fast Exporting from DataSet to Excel”和“Export Data to Excel Much Faster”。

【讨论】:

    【解决方案4】:

    查看Create Excel files from C# without office,因为这指的是使用EPPlus,它工作得很好——我能够从数据表中创建我的CSV数据,并在内存中批量加载Excel文件以流出。只需几行代码。 变量 csvData 是所有 csvData 的字符串值。

        using( ExcelPackage pck = new ExcelPackage( ) )
        {
          //Create the worksheet
          ExcelWorksheet ws = pck.Workbook.Worksheets.Add( "Sheet1" );
    
          // set the delimiter
          etf.Delimiter = ',';
          etf.EOL = "\n";
          etf.TextQualifier = "\"";
    
          //Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
          ws.Cells["A1"].LoadFromText( csvData, etf );
          return pck.GetAsByteArray( );
       }
    

    【讨论】:

      【解决方案5】:

      这里我使用 DataTable 将数据写入 excel 文件。我认为数据网格视图也与数据表相同。

      首先从数据库中获取数据:

      db.GetData(sqlgetprint);
      

      方法是调用方法:

      class DataBaseConnection
      {
          private OdbcConnection conn1 = new OdbcConnection(@"FILEDSN=C:/OTPub/Ot.dsn;" + "Uid=sa;" + "Pwd=otdata@123;"); //"DSN=Ot_DataODBC;" + "Uid=sa;" +  "Pwd=otdata@123;"
      
          //select
          public System.Data.DataTable GetData(string sql)
          {
              try
              {
      
                  conn1.Open();
                  OdbcDataAdapter adpt = new OdbcDataAdapter(sql, conn1);
                  DataTable dt = new DataTable();
                  adpt.Fill(dt);
                  conn1.Close();
                  return dt;
      
              }
              catch (Exception ex)
              {
                  conn1.Close();
                  throw ex;
              }
          }    
      }
      

      之后,在您的工作表单中为 DataBaseConncetion 类创建对象

       DataBaseConnection db = new DataBaseConnection();
      

      在您的按钮单击事件中,您可以编写此代码以写入 Excel 文件

      string sqlgetprint = "SELECT  Service_No,Full_name, Acc_No, OP_date, On_time, Off_time, OP_hours, Payment  FROM   Print_Op ORDER BY Service_No , OP_date";
                  DataTable dtall = db.GetData(sqlgetprint);
      
                  SaveFileDialog saveFileDialog1 = new SaveFileDialog();
                  saveFileDialog1.Filter = "Excel Documents (*.xls)|*.xls";
                  saveFileDialog1.FileName = "Employee Details.xls";
      
                  if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                  {
                      string fname = saveFileDialog1.FileName;
      
      
                      StreamWriter wr = new StreamWriter(fname);
                      for (int i = 0; i < dtall.Columns.Count; i++)
                      {
                          wr.Write(dtall.Columns[i].ToString().ToUpper() + "\t");
                      }
      
                      wr.WriteLine();
      
                      //write rows to excel file
                      for (int i = 0; i < (dtall.Rows.Count); i++)
                      {
                          for (int j = 0; j < dtall.Columns.Count; j++)
                          {
                              if (dtall.Rows[i][j] != null)
                              {
                                  wr.Write(Convert.ToString(dtall.Rows[i][j]) + "\t");
                              }
                              else
                              {
                                  wr.Write("\t");
                              }
                          }
                          //go to next line
                          wr.WriteLine();
                      }
                      //close file
                      wr.Close();
                      if (File.Exists(fname))
                      {
                          System.Diagnostics.Process.Start(fname);
                      }
      
                  }
              }
      
              catch (Exception)
              {
      
                  MessageBox.Show("Error Create Excel Sheet!");
              }
      

      【讨论】:

        【解决方案6】:

        这可能是更快的方式,

            using Excel = Microsoft.Office.Interop.Excel;
        
            public static void SaveGridToExcel(DataGridView DGV)
            {
        
                if (DGV.Rows.Count > 0)
                {
                    string filename = "";
                    SaveFileDialog SV = new SaveFileDialog();
                    SV.Filter = "EXCEL FILES|*.xlsx;*.xls";
                    DialogResult result = SV.ShowDialog();
        
        
                    if (result == DialogResult.OK)
                    {
        
                        filename = SV.FileName;
                        bool multiselect = DGV.MultiSelect;
                        DGV.MultiSelect = true;
                        DGV.SelectAll();
                        DGV.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
                        Clipboard.SetDataObject(DGV.GetClipboardContent());
                        var results = System.Convert.ToString(Clipboard.GetData(DataFormats.Text));
                        DGV.ClearSelection();
                        DGV.MultiSelect = multiselect;
                        Microsoft.Office.Interop.Excel.Application XCELAPP = null;
                        Microsoft.Office.Interop.Excel.Workbook XWORKBOOK = null;
                        Microsoft.Office.Interop.Excel.Worksheet XSHEET = null;
                        object misValue = System.Reflection.Missing.Value;
                        XCELAPP = new Excel.Application();
                        XWORKBOOK = XCELAPP.Workbooks.Add(misValue);
                        XCELAPP.DisplayAlerts = false;
                        XCELAPP.Visible = false;
                        XSHEET = XWORKBOOK.ActiveSheet;
                        XSHEET.Paste();
                        XWORKBOOK.SaveAs(filename, Excel.XlFileFormat.xlOpenXMLWorkbook);
                        XWORKBOOK.Close(false);
                        XCELAPP.Quit();
                        try
                        {                      
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(XSHEET);
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(XWORKBOOK);
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(XCELAPP);
                        }
                        catch { }
                    }
                }
            }
        

        【讨论】:

          【解决方案7】:

          对我来说,提高速度的最佳改进就是使用这种方式。

          private const string connectionString = "OLEDB;Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=MYDBSERVER;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=SERVERID;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=DBName"; 
          
          object misValue = System.Reflection.Missing.Value;
          Excel.Application xlsApp = new Excel.Application();
          Excel.Workbook xlsWorkbook = xlsApp.Workbooks.Add(misValue);
          Excel.Worksheet myWS = xlsWorkbook.Sheets[1];
          
           Excel.ListObject lo = myWS.ListObjects.AddEx(Excel.XlListObjectSourceType.xlSrcQuery, connectionString, true, 
           Excel.XlYesNoGuess.xlGuess, myWS.Range["A5"]);
           
          //A5 is the starting cell
          
          lo.QueryTable.CommandText = "SELECT * FROM TABLE";
          lo.Refresh();
          

          我尝试使用“for”循环(写入每个单元格)的 DataReader/Datatable 来恢复具有 5800 行(和 40 列)的查询都太慢了。数字:

          DataReader - 5:16 分钟
          这样 - 13 秒

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-01-15
            • 2014-11-21
            • 2010-10-01
            • 1970-01-01
            • 2010-10-01
            • 2016-04-20
            • 1970-01-01
            • 2018-01-10
            相关资源
            最近更新 更多