【问题标题】:Best /Fastest way to read an Excel Sheet into a DataTable?将 Excel 工作表读入数据表的最佳/最快方法?
【发布时间】:2013-01-10 15:43:52
【问题描述】:

我希望这里有人可以为我指明正确的方向 - 我正在尝试创建一个相当强大的实用程序,以尽快将 Excel 工作表(可能是 .xls 或 .xlsx)中的数据读取到 DataTable 中并且尽可能瘦。

我在 VB 中提出了这个例程(尽管我对 C# 的好答案同样满意):

Public Shared Function ReadExcelIntoDataTable(ByVal FileName As String, ByVal SheetName As String) As DataTable
    Dim RetVal As New DataTable

    Dim strConnString As String
    strConnString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=" & FileName & ";"

    Dim strSQL As String 
    strSQL = "SELECT * FROM [" & SheetName & "$]"

    Dim y As New Odbc.OdbcDataAdapter(strSQL, strConnString)

    y.Fill(RetVal)

    Return RetVal

End Function

我想知道这是否是最好的方法,或者是否有更好/更有效的方法(或者只是更智能的方法 - 也许是 Linq/本机 .Net 提供程序)来代替使​​用?

另外,只是一个快速而愚蠢的附加问题 - 我是否需要包含诸如 y.Dispose()y = Nothing 之类的代码,或者因为变量应该在例程结束时消失,所以需要处理,对吧?

谢谢!!

【问题讨论】:

  • 我会使用EPPlus,它是LoadFromDatatablestackoverflow.com/a/8309265/284240 注意它只支持xlsx
  • 谢谢,@Tim - 两个问题 - 1) 这对 .xls 有用吗?和 2) 这会证明比我目前的例行程序更快/更少占用资源吗?
  • 1.No 2.Maybe(它非常快)因为我不知道它并且它不支持旧的 excel 版本,所以我刚刚在这里发表了评论。
  • @TimSchmelter LoadFromDatatableEPPlus 如何将 Excel 文件加载到数据表中?据我所知,此方法支持将数据表写入Excel文件
  • @user838204:很好,我认为我当时误解了要求。如果您正在寻找一种方式,您可能想看看我曾经发布过的this approach

标签: c# .net vb.net


【解决方案1】:

如果你想在 C# 中基于 Ciaran Answer

做同样的事情
string sSheetName = null;
string sConnection = null;
DataTable dtTablesList = default(DataTable);
OleDbCommand oleExcelCommand = default(OleDbCommand);
OleDbDataReader oleExcelReader = default(OleDbDataReader);
OleDbConnection oleExcelConnection = default(OleDbConnection);

sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Test.xls;Extended Properties=\"Excel 12.0;HDR=No;IMEX=1\"";

oleExcelConnection = new OleDbConnection(sConnection);
oleExcelConnection.Open();

dtTablesList = oleExcelConnection.GetSchema("Tables");

if (dtTablesList.Rows.Count > 0) 
{
    sSheetName = dtTablesList.Rows[0]["TABLE_NAME"].ToString();
}

dtTablesList.Clear();
dtTablesList.Dispose();


if (!string.IsNullOrEmpty(sSheetName)) {
    oleExcelCommand = oleExcelConnection.CreateCommand();
    oleExcelCommand.CommandText = "Select * From [" + sSheetName + "]";
    oleExcelCommand.CommandType = CommandType.Text;
    oleExcelReader = oleExcelCommand.ExecuteReader();
    nOutputRow = 0;

    while (oleExcelReader.Read())
    {
    }
    oleExcelReader.Close();
}
oleExcelConnection.Close();

这是另一种在不使用 OLEDB 的情况下将 Excel 读入 DataTable 的方法 很快 请记住,文件 ext 必须是 .CSV 才能正常工作

private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
{
    csvData = new DataTable(defaultTableName);
    try
    {
        using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
        {
            csvReader.SetDelimiters(new string[]
            {
                tableDelim 
            });
            csvReader.HasFieldsEnclosedInQuotes = true;
            string[] colFields = csvReader.ReadFields();
            foreach (string column in colFields)
            {
                DataColumn datecolumn = new DataColumn(column);
                datecolumn.AllowDBNull = true;
                csvData.Columns.Add(datecolumn);
            }

            while (!csvReader.EndOfData)
            {
                string[] fieldData = csvReader.ReadFields();
                //Making empty value as null
                for (int i = 0; i < fieldData.Length; i++)
                {
                    if (fieldData[i] == string.Empty)
                    {
                        fieldData[i] = string.Empty; //fieldData[i] = null
                    }
                    //Skip rows that have any csv header information or blank rows in them
                    if (fieldData[0].Contains("Disclaimer") || string.IsNullOrEmpty(fieldData[0]))
                    {
                        continue;
                    }
                }
                csvData.Rows.Add(fieldData);
            }
        }
    }
    catch (Exception ex)
    {
    }
    return csvData;
}

【讨论】:

  • 你的意思是tableDelim
  • 这是一个静态的顶部声明为static string tableDelim = ",";
  • 上面第一个代码sn-p中的一些转换需要改一下。比如sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString;sSheetName = dtTablesList.Rows[0]["TABLE_NAME"].ToString();。还有oleExcelCommand.ExecuteReaderoleExcelCommand.ExecuteReader()oleExcelReader.ReadoleExcelReader.Read()
  • 感谢 CriticalException 如果您愿意,请随时编辑。我正在发布关键版本。谢谢
【解决方案2】:

我一直为此使用OLEDB,比如...

    Dim sSheetName As String
    Dim sConnection As String
    Dim dtTablesList As DataTable
    Dim oleExcelCommand As OleDbCommand
    Dim oleExcelReader As OleDbDataReader
    Dim oleExcelConnection As OleDbConnection

    sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Test.xls;Extended Properties=""Excel 12.0;HDR=No;IMEX=1"""

    oleExcelConnection = New OleDbConnection(sConnection)
    oleExcelConnection.Open()

    dtTablesList = oleExcelConnection.GetSchema("Tables")

    If dtTablesList.Rows.Count > 0 Then
        sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString
    End If

    dtTablesList.Clear()
    dtTablesList.Dispose()

    If sSheetName <> "" Then

        oleExcelCommand = oleExcelConnection.CreateCommand()
        oleExcelCommand.CommandText = "Select * From [" & sSheetName & "]"
        oleExcelCommand.CommandType = CommandType.Text

        oleExcelReader = oleExcelCommand.ExecuteReader

        nOutputRow = 0

        While oleExcelReader.Read

        End While

        oleExcelReader.Close()

    End If

    oleExcelConnection.Close()

ACE.OLEDB 提供程序将读取.xls.xlsx 文件,我一直发现速度相当不错。

【讨论】:

  • 太棒了!谢谢,@Ciaran - 我将代码更改为使用 OleDB 提供程序,看起来一切正常!谢谢!!
  • 此代码在我的本地计算机上运行,​​但是当我在服务器上发布它时 - 我收到错误“'Microsoft.ACE.OLEDB.12.0' 提供程序未在本地计算机上注册。 "。我从 "microsoft.com/en-us/download/details.aspx?id=13255".But 安装了同样的东西。
  • 版本地狱我怀疑,试试2007 redistributable instead
  • 我在 SQL/Server 集群上将它广泛用于 SSIS。只要您安装了驱动程序,它就会在服务器上运行。
【解决方案3】:
public DataTable ImportExceltoDatatable(string filepath)
{
    // string sqlquery= "Select * From [SheetName$] Where YourCondition";
    string sqlquery = "Select * From [SheetName$] Where Id='ID_007'";
    DataSet ds = new DataSet();
    string constring = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filepath + ";Extended Properties=\"Excel 12.0;HDR=YES;\"";
    OleDbConnection con = new OleDbConnection(constring + "");
    OleDbDataAdapter da = new OleDbDataAdapter(sqlquery, con);
    da.Fill(ds);
    DataTable dt = ds.Tables[0];
    return dt;
}

【讨论】:

  • 一些文字解释会很好
  • 为什么要将空字符串添加到constringOleDbConnection(constring + "");
  • @JDRay 实际上它应该可以在不添加空字符串的情况下工作,但是当我只通过“constring”时出现连接错误。这是添加空字符串的一种尝试方法。它对我有用。我不知道具体原因。
  • 这听起来就值得单独提出一个 SO 问题。
  • 完美运行
【解决方案4】:

这对我来说似乎工作得很好。

private DataTable ReadExcelFile(string sheetName, string path)
{

    using (OleDbConnection conn = new OleDbConnection())
    {
        DataTable dt = new DataTable();
        string Import_FileName = path;
        string fileExtension = Path.GetExtension(Import_FileName);
        if (fileExtension == ".xls")
            conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Import_FileName + ";" + "Extended Properties='Excel 8.0;HDR=YES;'";
        if (fileExtension == ".xlsx")
            conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + Import_FileName + ";" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'";
        using (OleDbCommand comm = new OleDbCommand())
        {
            comm.CommandText = "Select * from [" + sheetName + "$]";
            comm.Connection = conn;
            using (OleDbDataAdapter da = new OleDbDataAdapter())
            {
                da.SelectCommand = comm;
                da.Fill(dt);
                return dt;
            }
        }
    }
}

【讨论】:

  • if 条件不是必需的,因为 OLEDB 12.0 支持 .xls 和 .xlsx 格式。
  • 像魅力一样工作。
  • 这么快!我喜欢它。
【解决方案5】:

您可以将 OpenXml SDK 用于 *.xlsx 文件。它的工作速度非常快。我为这个 sdk 做了简单的 C# IDataReader 实现。见here。现在您可以轻松地将 excel 文件读取到 DataTable,并且可以将 excel 文件导入 sql server 数据库(使用 SqlBulkCopy)。 ExcelDataReader 读取速度非常快。在我的机器上,10000 条记录少于 3 秒,60000 条记录少于 8 秒。

读取数据表示例:

class Program
{
    static void Main(string[] args)
    {
        var dt = new DataTable();
        using (var reader = new ExcelDataReader(@"data.xlsx"))
            dt.Load(reader);

        Console.WriteLine("done: " + dt.Rows.Count);
        Console.ReadKey();
   }
}

【讨论】:

  • 这很奇怪...当我只用一个文件名调用您的 ExcelDataReader() 函数时,它正确读取了第一个工作表的数据。但是,当我提供工作表名称时,结果只是得到了一个空的 DataTable(没有错误、没有异常,并且 DataTable.Load() 运行正常)。奇怪...
  • 补充一下,我的问题只发生在顶部有 1 个或多个空白行的工作表上。代码中没有出现错误,一切运行良好,但 DataTable.Load() 只是读取了 0 行和 0 列的 DataTable。我可以使用 ExcelDataReader 很好地阅读所有其他工作表。
  • 感谢您的评论。我解决了这个问题。
【解决方案6】:

我发现这样很容易

    using System;
    using System.Data;
    using System.IO;
    using Excel;

    public DataTable ExcelToDataTableUsingExcelDataReader(string storePath)
    {
        FileStream stream = File.Open(storePath, FileMode.Open, FileAccess.Read);

        string fileExtension = Path.GetExtension(storePath);
        IExcelDataReader excelReader = null;
        if (fileExtension == ".xls")
        {
            excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
        }
        else if (fileExtension == ".xlsx")
        {
            excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
        }

        excelReader.IsFirstRowAsColumnNames = true;
        DataSet result = excelReader.AsDataSet();
        var test = result.Tables[0];
        return result.Tables[0];
    }

注意:您需要为此安装 SharpZipLib 包

Install-Package SharpZipLib

干净整洁! ;)

【讨论】:

  • 在这里提出的所有解决方案中,你的对我来说是最好的。它不需要使用导入功能在每台 PC 上安装第三方应用程序的 excel oledb。要使用您的解决方案,我不需要安装 SharpZipLib,而是安装 ExcelDataReader 和 ExcelDataReader.DataSet。同样,这一行“excelReader.IsFirstRowAsColumnNames = true;”不再工作了。
  • 不过这个很慢。
【解决方案7】:

这是从excel oledb中读取的方式

try
{
    System.Data.OleDb.OleDbConnection MyConnection;
    System.Data.DataSet DtSet;
    System.Data.OleDb.OleDbDataAdapter MyCommand;
    string strHeader7 = "";
    strHeader7 = (hdr7) ? "Yes" : "No";
    MyConnection = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fn + ";Extended Properties=\"Excel 12.0;HDR=" + strHeader7 + ";IMEX=1\"");
    MyCommand = new System.Data.OleDb.OleDbDataAdapter("select * from [" + wks + "$]", MyConnection);
    MyCommand.TableMappings.Add("Table", "TestTable");
    DtSet = new System.Data.DataSet();
    MyCommand.Fill(DtSet);
    dgv7.DataSource = DtSet.Tables[0];
    MyConnection.Close();
}
catch (Exception ex)
{
    MessageBox.Show(ex.ToString());
}

【讨论】:

  • hdr7 将是此方法的使用者传递的布尔值。 fn 将是 excel 文件。wks 将是 excel 文件的工作表名称(应该在使用它之前找到)底线:工作完成一半 :(
【解决方案8】:

下面的代码是我自己测试的,非常简单易懂,好用,速度快。 此代码最初采用所有工作表名称,然后将该 Excel 文件的所有表放入数据集中。

    public static DataSet ToDataSet(string exceladdress, int startRecord = 0, int maxRecord = -1, string condition = "")
    {
        DataSet result = new DataSet();
        using (OleDbConnection connection = new OleDbConnection(
                (exceladdress.TrimEnd().ToLower().EndsWith("x"))
                ? "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + exceladdress + "';" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'"
                : "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + exceladdress + "';Extended Properties=Excel 8.0;"))
            try
            {
                connection.Open();
                DataTable schema = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
                foreach (DataRow drSheet in schema.Rows)
                    if (drSheet["TABLE_NAME"].ToString().Contains("$"))
                    {
                        string s = drSheet["TABLE_NAME"].ToString();
                        if (s.StartsWith("'")) s = s.Substring(1, s.Length - 2);
                        System.Data.OleDb.OleDbDataAdapter command =
                            new System.Data.OleDb.OleDbDataAdapter(string.Join("", "SELECT * FROM [", s, "] ", condition), connection);
                        DataTable dt = new DataTable();
                        if (maxRecord > -1 && startRecord > -1) command.Fill(startRecord, maxRecord, dt);
                        else command.Fill(dt);
                        result.Tables.Add(dt);
                    }
                return result;
            }
            catch (Exception ex) { return null; }
            finally { connection.Close(); }
    }

享受...

【讨论】:

    【解决方案9】:
    ''' <summary>
    ''' ReadToDataTable reads the given Excel file to a datatable.
    ''' </summary>
    ''' <param name="table">The table to be populated.</param>
    ''' <param name="incomingFileName">The file to attempt to read to.</param>
    ''' <returns>TRUE if success, FALSE otherwise.</returns>
    ''' <remarks></remarks>
    Public Function ReadToDataTable(ByRef table As DataTable,
                                    incomingFileName As String) As Boolean
        Dim returnValue As Boolean = False
        Try
    
            Dim sheetName As String = ""
            Dim connectionString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & incomingFileName & ";Extended Properties=""Excel 12.0;HDR=No;IMEX=1"""
            Dim tablesInFile As DataTable
            Dim oleExcelCommand As OleDbCommand
            Dim oleExcelReader As OleDbDataReader
            Dim oleExcelConnection As OleDbConnection
    
            oleExcelConnection = New OleDbConnection(connectionString)
            oleExcelConnection.Open()
    
            tablesInFile = oleExcelConnection.GetSchema("Tables")
    
            If tablesInFile.Rows.Count > 0 Then
                sheetName = tablesInFile.Rows(0)("TABLE_NAME").ToString
            End If
    
            If sheetName <> "" Then
    
                oleExcelCommand = oleExcelConnection.CreateCommand()
                oleExcelCommand.CommandText = "Select * From [" & sheetName & "]"
                oleExcelCommand.CommandType = CommandType.Text
    
                oleExcelReader = oleExcelCommand.ExecuteReader
    
                'Determine what row of the Excel file we are on
                Dim currentRowIndex As Integer = 0
    
                While oleExcelReader.Read
                    'If we are on the First Row, then add the item as Columns in the DataTable
                    If currentRowIndex = 0 Then
                        For currentFieldIndex As Integer = 0 To (oleExcelReader.VisibleFieldCount - 1)
                            Dim currentColumnName As String = oleExcelReader.Item(currentFieldIndex).ToString
                            table.Columns.Add(currentColumnName, GetType(String))
                            table.AcceptChanges()
                        Next
                    End If
                    'If we are on a Row with Data, add the data to the SheetTable
                    If currentRowIndex > 0 Then
                        Dim newRow As DataRow = table.NewRow
                        For currentFieldIndex As Integer = 0 To (oleExcelReader.VisibleFieldCount - 1)
                            Dim currentColumnName As String = table.Columns(currentFieldIndex).ColumnName
                            newRow(currentColumnName) = oleExcelReader.Item(currentFieldIndex)
                            If IsDBNull(newRow(currentFieldIndex)) Then
                                newRow(currentFieldIndex) = ""
                            End If
                        Next
                        table.Rows.Add(newRow)
                        table.AcceptChanges()
                    End If
    
                    'Increment the CurrentRowIndex
                    currentRowIndex += 1
                End While
    
                oleExcelReader.Close()
    
            End If
    
            oleExcelConnection.Close()
            returnValue = True
        Catch ex As Exception
            'LastError = ex.ToString
            Return False
        End Try
    
    
        Return returnValue
    End Function
    

    【讨论】:

      【解决方案10】:

      使用下面的 sn-p 会很有帮助。

      string POCpath = @"G:\Althaf\abc.xlsx";
      
      string POCConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + POCpath + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=1\";";
      
      OleDbConnection POCcon = new OleDbConnection(POCConnection);
      OleDbCommand POCcommand = new OleDbCommand();
      DataTable dt = new DataTable();
      OleDbDataAdapter POCCommand = new OleDbDataAdapter("select * from [Sheet1$] ", POCcon);
      POCCommand.Fill(dt);
      Console.WriteLine(dt.Rows.Count);
      

      【讨论】:

        【解决方案11】:

        我用过这种方法,对我来说,它是如此高效和快速。

        // Step 1. Download NuGet source of Generic Parsing by Andrew Rissing
        // Step 2. Reference this to your project
        // Step 3. Reference Microsoft.Office.Interop.Excel to your project
        // Step 4. Follow the logic below
        
        public static DataTable ExcelSheetToDataTable(string filePath) {
        
            // Save a copy of the Excel file as CSV
            var xlApp = new XL.Application();
            var xlWbk = xlApp.Workbooks.Open(filePath);
            var tempPath =
                Path.Combine(Environment
                    .GetFolderPath(Environment.SpecialFolder.UserProfile)
                    , "AppData"
                    , "Local",
                    , "Temp"
                    , Path.GetFileNameWithoutExtension(filePath) + ".csv");
        
            xlApp.DisplayAlerts = false;
            xlWbk.SaveAs(tempPath, XL.XlFileFormat.xlCSV);
            xlWbk.Close(SaveChanges: false);
            xlApp.Quit();
        
            // The actual parsing
            using (var parser = new GenericParserAdapter(tempPath)) {
                parser.FirstRowHasHeader = true;
                return parser.GetDataTable();
            }
        
        }
        

        Generic Parsing by Andrew Rissing

        【讨论】:

          【解决方案12】:

          这是另一种方法

          public DataSet CreateTable(string source)
          {
              using (var connection = new OleDbConnection(GetConnectionString(source, true)))
              {
                  var dataSet = new DataSet();
                  connection.Open();
                  var schemaTable = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
                  if (schemaTable == null)
                      return dataSet;
          
                  var sheetName = "";
                  foreach (DataRow row in schemaTable.Rows)
                  {
                      sheetName = row["TABLE_NAME"].ToString();
                      break;
                  }
          
                  var command = string.Format("SELECT * FROM [{0}$]", sheetName);
                  var adapter = new OleDbDataAdapter(command, connection);
                  adapter.TableMappings.Add("TABLE", "TestTable");
                  adapter.Fill(dataSet);
                  connection.Close();
          
                  return dataSet;
              }
          }
          
          //
          
          private string GetConnectionString(string source, bool hasHeader)
          {
              return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};
              Extended Properties=\"Excel 12.0;HDR={1};IMEX=1\"", source, (hasHeader ? "YES" : "NO"));
          }
          

          【讨论】:

            猜你喜欢
            • 2020-09-30
            • 2012-10-22
            • 2011-02-23
            • 2020-08-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-11-09
            • 2011-01-29
            相关资源
            最近更新 更多