任何方式来加快这个Excel导入?

我有一个有约250000行,需要永久导入的Excel文档。 我已经做了许多变化的导入,但有一些要求: – 需要validation每个单元格中的数据 – 必须检查数据库中是否存在重复 – 如果存在重复,更新条目 – 如果不存在条目,插入一个新的

我尽可能多地使用了并行,但是我相信肯定有一些方法可以让这个导入运行得更快。 任何援助或想法将不胜感激。

请注意,数据库是在局域网上,是的,我知道我还没有使用参数化的SQL命令(还)。

public string BulkUserInsertAndUpdate() { DateTime startTime = DateTime.Now; try { ProcessInParallel(); Debug.WriteLine("Time taken: " + (DateTime.Now - startTime)); } catch (Exception ex) { return ex.Message; } return ""; } private IEnumerable<Row> ReadDocument() { using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(_fileName, false)) { WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart; Sheet ss = workbookPart.Workbook.Descendants<Sheet>().SingleOrDefault(s => s.Name == "User"); if (ss == null) throw new Exception("There was a problem trying to import the file. Please insure that the Sheet's name is: User"); WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id); OpenXmlReader reader = OpenXmlReader.Create(worksheetPart); StringTablePart = workbookPart.SharedStringTablePart; while (reader.Read()) { if (reader.ElementType == typeof(Row)) { do { if (reader.HasAttributes) { var rowNum = int.Parse(reader.Attributes.First(a => a.LocalName == "r").Value); if (rowNum == 1) continue; var row = (Row)reader.LoadCurrentElement(); yield return row; } } while (reader.ReadNextSibling()); // Skip to the next row break; // We just looped through all the rows so no need to continue reading the worksheet } } } } private void ProcessInParallel() { // Use ConcurrentQueue to enable safe enqueueing from multiple threads. var exceptions = new ConcurrentQueue<Exception>(); Parallel.ForEach(ReadDocument(), (row, loopState) => { List<Cell> cells = row.Descendants<Cell>().ToList(); if (string.IsNullOrEmpty(GetCellValue(cells[0], StringTablePart))) return; // validation code goes here.... try { using (SqlConnection connection = new SqlConnection("user id=sa;password=D3vAdm!n@;server=196.30.181.143;database=TheUnlimitedUSSD;MultipleActiveResultSets=True")) { connection.Open(); SqlCommand command = new SqlCommand("SELECT count(*) FROM dbo.[User] WHERE MobileNumber = '" + mobileNumber + "'", connection); var userCount = (int) command.ExecuteScalar(); if (userCount > 0) { // update command = new SqlCommand("UPDATE [user] SET NewMenu = " + (newMenuIndicator ? "1" : "0") + ", PolicyNumber = '" + policyNumber + "', Status = '" + status + "' WHERE MobileNumber = '" + mobileNumber + "'", connection); command.ExecuteScalar(); Debug.WriteLine("Update cmd"); } else { // insert command = new SqlCommand("INSERT INTO dbo.[User] ( MobileNumber , Status , PolicyNumber , NewMenu ) VALUES ( '" + mobileNumber + "' , '" + status + "' , '" + policyNumber + "' , " + (newMenuIndicator ? "1" : "0") + " )", connection); command.ExecuteScalar(); Debug.WriteLine("Insert cmd"); } } } catch (Exception ex) { exceptions.Enqueue(ex); Debug.WriteLine(ex.Message); loopState.Break(); } }); // Throw the exceptions here after the loop completes. if (exceptions.Count > 0) throw new AggregateException(exceptions); } 

我会build议你做一个批量导入,而不需要对中间表进行任何validation,然后才能通过SQL进行所有validation。 您的电子表格数据现在将与SQL表格类似。 这就是我所做的工业强度从Excel和CSVimport300万行+取得巨大成功。

大多数情况下,我build议你检查你的并行性是否最优。 由于你的瓶颈很可能是Excel文件上的磁盘IO和Sql服务器上的IO,所以我build议不要这样做。 你已经这两个过程中并行(所以每个过程的速度都是最慢的)。 你的并行线程将争夺数据库,并有可能放慢对方的速度。 有八个线程没有意义,如果你的硬盘不能跟上一个 – 它只会产生开销。

我build议两件事。 首先:排除所有的并行性,看看它是否真的有帮助。 如果单线程将整个文件parsing到内存中的单个队列中,那么将整个文件运行到数据库中,您可能会发现速度更快。

然后,我试着把它分成两个线程:一个处理传入文件到队列,一个从队列中取出项目并将它们推送到数据库中。 这样,每个缓慢的资源就有一个线程正在处理 – 所以你最大限度地减less了争用 – 而且每个线程只被一个资源阻塞 – 所以你要尽可能优化地处理资源。

这是multithreading编程的真正诀窍。 在一个问题上抛出额外的线程不一定会提高性能。 你要做的是尽量减less程序等待外部(如磁盘或networkingIO)完成的空闲时间。 如果一个线程在Excel文件上等待,并且一个线程在SQL服务器上等待,而且它们之间的操作是最小的(在你的情况下,你会发现你的代码将运行得像那些外部资源将允许它。

另外,你自己提到它,但是使用参数化的Sql不仅仅是一件很酷的事情,它会提高你的性能。 目前,您正在为每个插入创build一个新的SqlCommand ,这有开销。 如果切换到参数化命令,则可以始终保持相同的命令,只需更改参数值,这将为您节省一些时间。 我不认为这是可能的并行ForEach(我怀疑你可以在线程间重用SqlCommand ),但它可以很好地使用上述任何一种方法。

增强处理的一些提示(因为我相信这是你所需要的,而不是一个代码修复)。

  • 有Excel预先检查重复的行。 这是淘汰过时工具的一个非常体面的工具。 如果A和B是重复的,你会创buildA然后用B的数据更新。 这样,你可以删除A,只能创buildB.
  • 不要将其处理为.xls(x)文件,将其转换为CSV。 (如果你还没有)。
  • 在数据库上创build一些存储过程。 我通常不喜欢存储过程在简单的数据检索项目中使用时,但它的自动化脚本需要有效运行奇迹。 只需添加一个Create函数(我假设在删除重复项(在提示1中)之后,更新函数将不再需要)+

我不确定的一些提示将有助于您的具体情况:

  • 使用LINQ而不是创build命令string。 LINQ自动调整你的查询。 但是,突然转换到LINQ并不是你可以在瞬间完成的,所以你需要超出你需要的多less。
    • 我知道你说在数据库服务器上没有Excel,但是你可以用数据库进程的.csv文件代替,不需要为csv文件安装软件。 你可以看看下面的内容: http : //dev.mysql.com/doc/refman/5.1/en/load-data.html