3

2 つのタブがある Excel シートがあります。

1 つは約 700 k で、もう 1 つは約 25 k です。問題は、ファイルを読み込んでいるときにメモリが使い果たされてクラッシュすることです! 100 万行を超える巨大なファイルを処理する方法。

ここに私が使用している私の現在のコードがあります:

  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

大量のデータをメモリにロードする必要があるため (つまり、1M 行 * 1 行あたり 1Kb ~ 1GB)、合理的な唯一のオプションは、x86 アプリケーションのアドレス空間制限としてビルド 64 ビット (x64) アプリケーションを使用することです (通常の x86 システムでは 2GB、最大x64 システムで 4GB まで) では、十分なメモリを割り当てることができません。

ノート:

  • 場合によっては x64 に移行できない場合があります (つまり、ある種の PInvoke を介して使用される x64 ネイティブ ライブラリの欠如)。
  • 慎重に設計された行のカスタム クラスを使用すると、x86 プロセスでより多くの行をメモリに収めることができる場合があります。つまり、一部の値は文字列として DB に存在する場合があり、メモリ内の文字列ではなく、1 ~ 4 バイトの長さの列挙型として表すことができます。
  • カスタムのメモリ内検索を使用する代わりに、検索を DB/Excel に移動することを検討してください。
  • x86 に固執する場合は、メモリにデータの複数のコピーをロードしないようにしてください。
于 2013-07-30T00:22:27.883 に答える