老系统Excel数据导入优化10w数据

Posted yanghucheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了老系统Excel数据导入优化10w数据相关的知识,希望对你有一定的参考价值。

  1. 背景:老系统asp.net 2.0项目使用客户反应,某个业务每个月导入数据操作很慢,大致需要15-30分钟才会导入完毕;
  2. 分析:导入慢的原因:

 .数据量过大,且采用的是同步,单个excel sheet 13万+数据;

 .导入前验证,每行某列 验证,频繁和数据库交互

 .使用很老的微软企业库进行批量插入操作,效率低下 ,插入后,又批量进行执行sql修改操作  ;

3 解决方案:

                 修改excel转table 的方案由OpenXMLHelper 转换变为 NPOI;

                  

技术分享图片
  1   public class NPOIHelper
  2     {
  3         /// <summary>
  4         /// 将excel导入到datatable
  5         /// </summary>
  6         /// <param name="filePath">excel路径</param>
  7         /// <param name="isColumnName">第一行是否是列名</param>
  8         /// <returns>返回datatable</returns>
  9         public DataTable ExcelToDataTable(string filePath, bool isColumnName)
 10         {
 11             DataTable dataTable = null;
 12             FileStream fs = null;
 13             DataColumn column = null;
 14             DataRow dataRow = null;
 15             IWorkbook workbook = null;
 16             ISheet sheet = null;
 17             IRow row = null;
 18             ICell cell = null;
 19             int startRow = 0;
 20             try
 21             {
 22                 using (fs = File.OpenRead(filePath))
 23                 {
 24                     // 2007版本
 25                     if (filePath.IndexOf(".xlsx") > 0)
 26                         workbook = new XSSFWorkbook(fs);
 27                     // 2003版本
 28                     else if (filePath.IndexOf(".xls") > 0)
 29                         workbook = new HSSFWorkbook(fs);
 30 
 31                     if (workbook != null)
 32                     {
 33                         sheet = workbook.GetSheetAt(0);//读取第一个sheet,当然也可以循环读取每个sheet
 34                         dataTable = new DataTable();
 35                         if (sheet != null)
 36                         {
 37                             int rowCount = sheet.LastRowNum;//总行数
 38                             if (rowCount > 0)
 39                             {
 40                                 IRow firstRow = sheet.GetRow(0);//第一行
 41                                 int cellCount = firstRow.LastCellNum;//列数
 42 
 43                                 //构建datatable的列
 44                                 if (isColumnName)
 45                                 {
 46                                     startRow = 1;//如果第一行是列名,则从第二行开始读取
 47                                     for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
 48                                     {
 49                                         cell = firstRow.GetCell(i);
 50                                         if (cell != null)
 51                                         {
 52                                             if (cell.StringCellValue != null)
 53                                             {
 54                                                 column = new DataColumn(cell.StringCellValue);
 55                                                 dataTable.Columns.Add(column);
 56                                             }
 57                                         }
 58                                     }
 59                                 }
 60                                 else
 61                                 {
 62                                     for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
 63                                     {
 64                                         column = new DataColumn("column" + (i + 1));
 65                                         dataTable.Columns.Add(column);
 66                                     }
 67                                 }
 68 
 69                                 //填充行
 70                                 for (int i = startRow; i <= rowCount; ++i)
 71                                 {
 72                                     row = sheet.GetRow(i);
 73                                     if (row == null) continue;
 74 
 75                                     dataRow = dataTable.NewRow();
 76                                     for (int j = row.FirstCellNum; j < cellCount; ++j)
 77                                     {
 78                                         cell = row.GetCell(j);
 79                                         if (cell == null)
 80                                         {
 81                                             dataRow[j] = "";
 82                                         }
 83                                         else
 84                                         {
 85                                             //CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
 86                                             switch (cell.CellType)
 87                                             {
 88                                                 case CellType.BLANK:
 89                                                     dataRow[j] = "";
 90                                                     break;
 91                                                 case CellType.NUMERIC:
 92                                                     short format = cell.CellStyle.DataFormat;
 93                                                     //对时间格式(2015.12.5、2015/12/5、2015-12-5等)的处理
 94                                                     if (format == 14 || format == 31 || format == 57 || format == 58)
 95                                                         dataRow[j] = cell.DateCellValue;
 96                                                     else
 97                                                         dataRow[j] = cell.NumericCellValue;
 98                                                     break;
 99                                                 case CellType.STRING:
100                                                     dataRow[j] = cell.StringCellValue;
101                                                     break;
102                                             }
103                                         }
104                                     }
105                                     if (dataRow == null)  Logger.Write(string.Format("转换行失败,行数为:{0}", i.ToString()));
106                                     dataTable.Rows.Add(dataRow);
107                                 }
108                             }
109                             else 
110                             {
111                                 Logger.Write(string.Format("转换datarow完毕,行数:{0}", rowCount.ToString()));
112                             }
113                         }
114                     }
115                     else 
116                     {
117                         Logger.Write("转换workbook 为空");
118                     }
119                 }
120                 return dataTable;
121             }
122             catch (Exception ex)
123             {
124                 Logger.Write(string.Format("转换失败,异常:{0}", ex.ToString()));
125                 if (fs != null)
126                 {
127                     fs.Close();
128                 }
129                 return null;
130             }
131         }
132     }
NPOI

                         去掉excel的输入验证,由于只是验证数据库是否存在该编码,所以改为由存储过程内连接过滤

                         批量插入修改为使用 SqlBulkCopy,首先创建一张临时表存储需要插入的excel数据(未过滤)

技术分享图片
 public int CreateTempTable()
        {
            string createSql = @"
                    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N‘[dbo].[Temp_gdzc]‘) AND type in (N‘U‘))
                    delete FROM [dbo].[Temp_gdzc]
                    IF Not EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N‘[dbo].[Temp_gdzc]‘) AND type in (N‘U‘))
                    BEGIN
                    CREATE TABLE [dbo].[Temp_gdzc](
                        [公司代码] [nvarchar](100) NULL,
                        [资产编号] [nvarchar](100) NULL,
                        [资产次级编号] [nvarchar](100) NULL,
                        [资产描述] [nvarchar](255) NULL,
                        [资产管理序列号] [nvarchar](100) NULL,
                        [资本化日期] [datetime] NULL,
                        [资产原值] [float] NULL,
                        [资产累计折旧] [float] NULL,
                        [资产净值] [float] NULL
                    ) 
                    end 
            ";

            return BusinessRules.Common.SqlHelperBatch.ExecuteNonQuery(createSql, new SqlParameter[] { });

        }
创建临时表

       然后根据临时表 和 要插入的表的数据内关联过滤无效数据,调用存储过程使用 Insert into  select 插入;

技术分享图片
 1         //调用存储过程插入明细表
 2                 var parameters = new List<SqlParameter>();
 3                 parameters.Add(new SqlParameter("@MainID", SqlDbType.BigInt, 8) { Value = Mainid });
 4                 parameters.Add(new SqlParameter("@CloseDate", SqlDbType.DateTime) { Value = closeTemp });
 5                 SqlParameter outParameter = new SqlParameter("@ResultCount", SqlDbType.Int, 8);
 6                 outParameter.Direction = ParameterDirection.Output;
 7                 parameters.Add(outParameter);
 8 
 9                 var result = BusinessRules.Common.SqlHelperBatch.ExecuteNonQuery(tran, CommandType.Text, "exec P_AssetBalanceQuiry_Insert @MainID, @CloseDate,@ResultCount out", parameters.ToArray());
10                 count = Convert.ToInt32(outParameter.Value == DBNull.Value ? 0 : outParameter.Value);
调用

                       存储过程如下:

技术分享图片批量插入存储过程

                      最后批量修改再使用 存储过程 执行关联修改;

技术分享图片
CREATE PROCEDURE [dbo].[P_T_UpdateA]  
  @MainID int   
AS  
BEGIN  
  UPDATE T1 SET A=C from T2 
where A1= B1 AND Main_ID = @MainID  
END  
批量修改

                    需要注意的地方则是:批量插入时,主表ID需要记录,由于是一次操作,只会有一个主表ID,所以会先插入主表,得到主表ID,再批量插入从表;

最终优化操作时间由10分钟 到5-10秒;

 

 

                    

以上是关于老系统Excel数据导入优化10w数据的主要内容,如果未能解决你的问题,请参考以下文章

如何用java导入Excel数据到数据库?

用php导入10W条+ 级别的csv大文件数据到mysql。导出10W+级别数据到csv文件

导入不同业务数据通过Excel实现

python实例:导入会员数据后,读取数据文件,检查导入正确性(整列取excel值合并列response取值)

后端一次性传了10w条数据,前端该如何处理?—— 面试高频

后端一次性传了10w条数据,前端该如何处理?—— 面试高频