72876


Googleで検索して答えが出てこなかったため、こちらで質問させていただきます。

C#のアプリケーションでExcelファイルを開き、内容をXMLで出力するアプリケーションを開発しています。  
開く対象のExcelファイルは2つのシートで構成されています。  
1枚目は100,000行300列ほどある大容量のシート  
2枚目は100行20列ほどのシートになります。

2枚目のシートを使いたいのですが、1枚目のシートが大容量であるために開くのに時間がかかります。  
試したライブラリは標準のExcel用Interop, EPPlus 4.x, ClosedXMLです。  
この中ではEPPlusが一番はやいのですが、それでも90秒程度かかりました。

上記の場合、高速にアクセスする手段は他に考えられますか?

※前提条件として、1book2sheets固定です。複数のbookにすることはできないです。  
また、Excelをやめる ということも現状できないこととします。


CC BY-SA 4.0
2020-12-23T06:48:59.643
72835
2020-12-25T01:55:42.643
2020-12-23T14:25:45.503
12388
12388
question
c# excel
大容量シートを含むExcelファイルを高速で開く方法


Interopでも遅い、つまりExcel本体より早く開きたいという要望自体が無茶ではないでしょうか？

[Excel のパフォーマンス: 計算パフォーマンスの強化](https://docs.microsoft.com/ja-
jp/office/vba/excel/concepts/excel-performance/excel-improving-calculation-
performance)
など、Excelファイルそのものを改善するか、それでも無理な場合は、Access等のデータベースに移行することを検討されてはどうでしょうか。


CC BY-SA 4.0
2020-12-23T09:50:38.243
72840
2020-12-23T09:50:38.243
4236
72835
answer


未検証ですが、Excelファイルを事前に加工するという力業があります。

xlsxファイルの中身は、主にxmlファイルが格納されたzipファイルになっています。  
`xl/workbook.xml`にシートの一覧情報があります。

[code]

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
    ...
      <sheets>
        <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
        <sheet name="Sheet2" sheetId="2" r:id="rId2"/>
        <sheet name="Sheet3" sheetId="3" r:id="rId3"/>
      </sheets>
    ...
    </workbook>
    
[/code]

不要な(大容量の)シートのsheetタグを削除してxlsxファイルに再格納すれば、そのシートが存在しない状態のxlsxファイルになります。  
zipファイルとxmlファイルの読み書きなので、.NETであれば豊富にサンプルがあるかと思います。

この方法には制約がありますので、質問者の環境で試して頂くのがいいかと思います。

  * シート名は事前にわかっていないといけない。
  * 残す方のシートが、削除する方のシートを参照していてはいけない。(数式や名前など)


CC BY-SA 4.0
2020-12-24T09:54:29.027
72864
2020-12-24T09:54:29.027
9113
72835
answer


ライブラリを使って読み込む速度比較としては[ExcelDataReader](https://github.com/ExcelDataReader/ExcelDataReader)が一番早い(※)と思います。

下記のサンプルコードでは  
`A1`から`Z65535`まで1-26の整数の入っている`Sheet1`と、  
`A1`に`ほげ`と書かれただけの`Sheet2`が存在する  
`Book1.xlsx`を読み込んでいます。

Office2016のInterop, EPPlus, NPOI, ClosedXML, ExcelDataReaderを使用して速度計測を行いました。  
EPPlus以降は2020年のクリスマス時点でNugetから取得した最新のライブラリを使用しています。

※ExcelDataReaderには、DataSetとして全シート全セルの情報を取得する方法と`NextResult`でシートを読み飛ばす方法があります。今回は後者の方法を利用しました。

**実行結果**

[code]

    1回目
    Interop: 3244 ms.
    EPPlus: 4396 ms.
    NPOI: 11008 ms.
    ClosedXML: 19601 ms.
    ExcelDataReader: 1605 ms.
    
    2回目
    Interop: 3257 ms.
    EPPlus: 4619 ms.
    NPOI: 11130 ms.
    ClosedXML: 20645 ms.
    ExcelDataReader: 1771 ms.
    
[/code]

**サンプルコード**

[code]

    using ClosedXML.Excel;
    using ExcelDataReader;
    using NPOI.SS.UserModel;
    using OfficeOpenXml;
    using System;
    using System.Diagnostics;
    using System.IO;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var path = @"Book1.xlsx";
                var sheetName = "Sheet2";
                var stopwatch = new Stopwatch();
    
                // Microsoft.Office.Interop
                stopwatch.Start();
                var app = new Microsoft.Office.Interop.Excel.Application();
                app.Visible = false;
                var oBook = (app.Workbooks.Open(path));
                var oSheet = oBook.Sheets[sheetName];
                Console.WriteLine(oSheet.Cells(1, 1).Text);
                oBook.Close();
                app.Quit();
                Console.WriteLine("Interop: {0} ms.", stopwatch.ElapsedMilliseconds);
    
                // EPPlus
                stopwatch.Restart();
                var fi = new FileInfo(path);
                ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
                using (var package = new ExcelPackage(fi))
                {
                    var sheet = package.Workbook.Worksheets[sheetName];
                    Console.WriteLine(sheet.GetValue(1, 1));
                }
                Console.WriteLine("EPPlus: {0} ms.", stopwatch.ElapsedMilliseconds);
    
                // NPOI
                stopwatch.Restart();
                var nBook = WorkbookFactory.Create(path);
                var nSheet = nBook.GetSheet(sheetName);
                Console.WriteLine(nSheet.GetRow(0).Cells[0].StringCellValue);
                nBook.Close();
                Console.WriteLine("NPOI: {0} ms.", stopwatch.ElapsedMilliseconds);
    
                // ClosedXML
                stopwatch.Restart();
                using (var book = new XLWorkbook(path))
                {
                    var sheet = book.Worksheet(sheetName);
                    Console.WriteLine(sheet.Cell(1, 1).Value);
                }
                Console.WriteLine("ClosedXML: {0} ms.", stopwatch.ElapsedMilliseconds);
    
                // ExcelDataReader
                stopwatch.Restart();
                using (var stream = new FileStream(path, FileMode.Open))
                using (var reader = ExcelReaderFactory.CreateReader(stream))
                {
                    while (reader.Name != sheetName)
                    {
                        reader.NextResult();
                    }
                    reader.Read();
                    Console.WriteLine(reader.GetValue(0));
                }
                Console.WriteLine("ExcelDataReader: {0} ms.", stopwatch.ElapsedMilliseconds);
    
                Console.ReadLine();
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2020-12-25T01:55:42.643
72876
2020-12-25T01:55:42.643
9820
72835
answer