任何方法可以加快这个Excel导入?(Any way to speed up this excel

2019-07-31 02:27发布

我有一个有大约25万行这需要永远导入Excel文件。 我这样做进口的许多变化,但有几个要求: - 需要在每个小区来验证数据 - 必须检查是否在数据库中存在重复的 - 如果存在重复,更新条目 - 如果条目不存在,插入一个新的

我已经使用并行尽可能但是我相信一定有办法让这个进口以更快的速度运行。 任何帮助或想法,将不胜感激。

请注意,数据库是一个局域网上,是的,我知道我没有使用参数化的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);

        }

Answer 1:

我会建议你做一个批量导入,没有任何验证,以中间表,然后才通过SQL做的所有验证。 您的电子表格的数据,现在将在一个类似的结构作为SQL表。 这就是我与工业实力的进口300万行+从Excel和CSV取得了巨大成功完成。



Answer 2:

天色我建议你检查你的并行性是最佳的。 由于您的瓶颈可能是磁盘IO上的Excel文件和IO到SQL服务器,我认为它可能不是。 你这两个进程并行化(所以他们每个人降低到最慢的速度); 您的并行线程将争取在数据库中,并可能减缓下来的海誓山盟。 有有(比方说)八个线程,如果你的硬盘无法与一个跟上没有意义 - 它只是创造开销。

有两件事情我倒是建议。 第一:取出所有的并行性,看看它的实际帮助。 如果单螺纹整个文件解析到内存中的单个队列,然后运行整个事情到数据库中,你会发现它的速度更快。

然后,我会尝试拆分到只有两个线程:一个处理传入的文件到队列,以及一个从队列中取物品,并将其推入数据库。 这样你每次你正在处理慢资源一个线程 - 让你最大限度地减少了竞争 - 而且每个线程仅由一个资源封锁 - 所以你正在处理该资源尽可能优化。

这是多线程编程的真正的诀窍。 在一个问题抛出额外的线程不一定提高性能。 你正在试图做的是尽量减少你的程序空转等待外在的东西(如磁盘或网络IO)完成的时间。 如果一个线程等待在Excel文件,和一个线程等待在SQL服务器上,而他们在做什么之间是最小的(在你的情况下,),你会发现你的代码将尽可能快地运行这些外部资源将允许它。

另外,你自己提到它,但使用参数化的SQL是不是只是想指出一个很酷的事情:它会增加你的表现。 此刻,你正在创建一个新SqlCommand对于每次插入,其开销。 如果切换到一个参数化的命令,你可以保持相同的指令吞吐量和只是改变参数值,这将节省您的时间。 我不认为这是有可能在平行的ForEach(我怀疑你可以重复使用SqlCommand跨线程),但它会很好地工作或者上面的方法的。



Answer 3:

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

  • 让Excel检查重复的行提前。 它是淘汰过时的工具,一个真正像样的工具。 如果A和B是重复的,你需要创建一个然后用B的数据进行更新。 这样一来,就可以淘汰一个,只有创造B.
  • 不处理它为.xls(x)的文件,将其转换为CSV。 (如果你还没有的话)。
  • 创建数据库的一些存储过程。 我在项目简单的数据检索时通常不喜欢存储过程,但它的工程奇迹,需要有效地运行自动化的脚本。 只需添加一个创建函数(我假设的更新功能是不必要的你已经淘汰后出副本(在提示1))+

一些提示我不知道会帮助您的具体situtation:

  • 使用LINQ,而不是创建命令字符串。 LINQ自动fintunes查询。 然而,突然切换到LINQ是不是你可以在一眨眼的功夫做的,所以你需要超过努力对你是多么需要它。
    • 我知道你说的没有Excel中的数据库服务器上,但你可以有数据库进程的.csv文件来代替,没有必要为CSV文件,安装的软件。 你可以看看下面: http://dev.mysql.com/doc/refman/5.1/en/load-data.html


文章来源: Any way to speed up this excel import?