3

我有一个带有两个选项卡的 Excel 表。

一个约为 700 k,另一个约为 25k。问题是当我加载文件时,我的内存被耗尽并且崩溃了!如何处理大文件,因为有些文件甚至可能超过一百万行。

这是我正在使用的当前代码:

  OleDbConnection cnn = new OleDbConnection("provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + fileName + "';Extended Properties=Excel 12.0;");
                cnn.Open();

                string qry = "SELECT * FROM [Detail$]";
                OleDbDataAdapter odp = new OleDbDataAdapter(qry, cnn);
                odp.Fill(detailTable);
                DataSet tmp = new DataSet();
                if (detailTable.Rows.Count > 0)
                {
                    Console.WriteLine("Total " + detailTable.Rows.Count + " Detail rows Loaded");
                    // MessageBox.Show("Input Sheet UPLOADED !");

                }
                qry = "SELECT * FROM [Gallery$]";
                OleDbDataAdapter odp1 = new OleDbDataAdapter(qry, cnn);
                odp1.Fill(galleryTable);
                if (galleryTable.Rows.Count > 0)
                {
                    Console.WriteLine("Total " + galleryTable.Rows.Count + " Gallery Numbers Loaded");
                    //  MessageBox.Show("Input Sheet UPLOADED !");

                }
4

3 回答 3

3

好的,我可以建议您使用类的DbDataAdapter.Fill(Int32, Int32, DataTable[])重载方法DbDataAdapter以“块”模式工作:

public int Fill(
    int startRecord,
    int maxRecords,
    params DataTable[] dataTables
)

使用此方法和我的代码示例,您可以一次处理大量行,而不是使用内存中的完整 excel 数据。每次填充后,处理您的临时数据表对象,您将能够通过这种方式避免内存泄漏。

您可以这样做:

        const string fileName = "myData.xlsx";
        const string excelConnString = "provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + fileName + "';Extended Properties=Excel 12.0;";

        using (var cnn = new OleDbConnection(excelConnString))
        {
            cnn.Open();

            const string countQuery = "SELECT COUNT(*) FROM [Detail$]";
            using (var cmd = new OleDbCommand(countQuery, cnn))
            {
                using (var reader = cmd.ExecuteReader())
                {
                    if (reader == null) return;

                    reader.Read();
                    var rowsCount = ((int)reader[0]);

                    const string query = "SELECT * FROM [Detail$]";
                    using (var odp = new OleDbDataAdapter(query, cnn))
                    {
                        var detailTable = new DataTable();
                        var recordToStartFetchFrom = 0; //zero-based record number to start with.
                        const int chunkSize = 100;
                        while (recordToStartFetchFrom <= rowsCount)
                        {
                            var diff = rowsCount - recordToStartFetchFrom;
                            int internalChunkSize = diff < 100 ? diff : chunkSize;
                            odp.Fill(recordToStartFetchFrom, internalChunkSize, detailTable);

                            foreach (DataRow row in detailTable.Rows)
                            {
                                Console.WriteLine("{1} {0}", row.ItemArray[0], row.ItemArray[1]);
                            }

                            Console.WriteLine("--------- {0}-{1} Rows Processed ---------", recordToStartFetchFrom, recordToStartFetchFrom + internalChunkSize);

                            recordToStartFetchFrom += chunkSize;

                            detailTable.Dispose();
                            detailTable = null;
                            detailTable = new DataTable();
                        }
                    }
                    Console.ReadLine();
                }
            }
        }
于 2013-07-30T00:21:58.577 回答
1

只选择您需要的 5 或 6 列。

于 2013-07-30T02:24:43.467 回答
1

由于您需要在内存中加载大量数据(即 1M 行 * 每行 1Kb ~ 1GB),您唯一合理的选择是使用构建 64 位(x64)应用程序作为 x86 应用程序的地址空间限制(在普通 x86 系统上为 2GB,最多在 x64 系统上到 4GB)将不允许分配足够的内存。

笔记:

  • 在某些情况下可能无法使用 x64(即缺少通过某种 PInvoke 使用的 x64 本机库)
  • 如果使用精心设计的行自定义类,您可以在 x86 进程中在内存中容纳更多行。即某些值可能以字符串形式存在于您的数据库中,并且可以表示为 1-4 字节长的枚举,而不是内存中的字符串。
  • 考虑将搜索移至 DB/Excel,而不是使用自定义内存搜索。
  • 如果坚持使用 x86,请确保不要在内存中加载超过 1 个数据副本。
于 2013-07-30T00:22:27.883 回答