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

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

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

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

3 解决方案:

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

                  

   public class NPOIHelper
{
/// <summary>
/// 将excel导入到datatable
/// </summary>
/// <param name="filePath">excel路径</param>
/// <param name="isColumnName">第一行是否是列名</param>
/// <returns>返回datatable</returns>
public DataTable ExcelToDataTable(string filePath, bool isColumnName)
{
DataTable dataTable = null;
FileStream fs = null;
DataColumn column = null;
DataRow dataRow = null;
IWorkbook workbook = null;
ISheet sheet = null;
IRow row = null;
ICell cell = null;
int startRow = ;
try
{
using (fs = File.OpenRead(filePath))
{
// 2007版本
if (filePath.IndexOf(".xlsx") > )
workbook = new XSSFWorkbook(fs);
// 2003版本
else if (filePath.IndexOf(".xls") > )
workbook = new HSSFWorkbook(fs); if (workbook != null)
{
sheet = workbook.GetSheetAt();//读取第一个sheet,当然也可以循环读取每个sheet
dataTable = new DataTable();
if (sheet != null)
{
int rowCount = sheet.LastRowNum;//总行数
if (rowCount > )
{
IRow firstRow = sheet.GetRow();//第一行
int cellCount = firstRow.LastCellNum;//列数 //构建datatable的列
if (isColumnName)
{
startRow = ;//如果第一行是列名,则从第二行开始读取
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
cell = firstRow.GetCell(i);
if (cell != null)
{
if (cell.StringCellValue != null)
{
column = new DataColumn(cell.StringCellValue);
dataTable.Columns.Add(column);
}
}
}
}
else
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
column = new DataColumn("column" + (i + ));
dataTable.Columns.Add(column);
}
} //填充行
for (int i = startRow; i <= rowCount; ++i)
{
row = sheet.GetRow(i);
if (row == null) continue; dataRow = dataTable.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
cell = row.GetCell(j);
if (cell == null)
{
dataRow[j] = "";
}
else
{
//CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
switch (cell.CellType)
{
case CellType.BLANK:
dataRow[j] = "";
break;
case CellType.NUMERIC:
short format = cell.CellStyle.DataFormat;
//对时间格式(2015.12.5、2015/12/5、2015-12-5等)的处理
if (format == || format == || format == || format == )
dataRow[j] = cell.DateCellValue;
else
dataRow[j] = cell.NumericCellValue;
break;
case CellType.STRING:
dataRow[j] = cell.StringCellValue;
break;
}
}
}
if (dataRow == null) Logger.Write(string.Format("转换行失败,行数为:{0}", i.ToString()));
dataTable.Rows.Add(dataRow);
}
}
else
{
Logger.Write(string.Format("转换datarow完毕,行数:{0}", rowCount.ToString()));
}
}
}
else
{
Logger.Write("转换workbook 为空");
}
}
return dataTable;
}
catch (Exception ex)
{
Logger.Write(string.Format("转换失败,异常:{0}", ex.ToString()));
if (fs != null)
{
fs.Close();
}
return null;
}
}
}

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 插入;

         //调用存储过程插入明细表
var parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("@MainID", SqlDbType.BigInt, ) { Value = Mainid });
parameters.Add(new SqlParameter("@CloseDate", SqlDbType.DateTime) { Value = closeTemp });
SqlParameter outParameter = new SqlParameter("@ResultCount", SqlDbType.Int, );
outParameter.Direction = ParameterDirection.Output;
parameters.Add(outParameter); var result = BusinessRules.Common.SqlHelperBatch.ExecuteNonQuery(tran, CommandType.Text, "exec P_AssetBalanceQuiry_Insert @MainID, @CloseDate,@ResultCount out", parameters.ToArray());
count = Convert.ToInt32(outParameter.Value == DBNull.Value ? : outParameter.Value);

调用

存储过程如下:


CREATE PROCEDURE [dbo].[P_AssetBalanceQuiry_Insert]
@MainID int ,
@CloseDate datetime,
@ResultCount int=0 output
AS
BEGIN
INSERT INTO T_AssetBalanceQuiry
(
CloseDate,CompanyCode,AssetsCode,AssetsDescribtion,
AssetsNumber,CapitalizationDate,AssetsOldValue,AssetsDepreciation,
AssetsNetWorth,ServiceLife,AssetsType,Main_ID,AssetsCode_sub,Status
)
select @CloseDate,公司代码,CONVERT(decimal(18, 0), 资产编号),资产描述,
管理序列号,资本化日期,资产原值,资产累计折旧,
资产净值, datediff(mm,资本化日期,getdate()),AS_PrimaryNo,@MainID,资产次级编号,null
from dbo.Temp_gdzc
inner join T_Asset on 资产管理序列号 = AS_MSNO;
select @ResultCount = count(1) from T_AssetBalanceQuiry where Main_ID = @MainID;
update T_AssetBalanceMain SET AssetsCount = @ResultCount where ID = @MainID;
END

批量插入存储过程

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

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秒;

                    

老系统优化同步导入10w+Excel数据 秒级的更多相关文章

  1. Redis实战--使用Jedis实现百万数据秒级插入

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 当我们 ...

  2. SQL Server大量数据秒级插入/新增/删除

    原文:SQL Server大量数据秒级插入/新增/删除 1.快速保存,该方法有四个参数,第一个参数为数据库连接,第二个参数为需要保存的DataTable,该参数的TableName属性需要设置为数据库 ...

  3. NPOI导入导出Excel数据

    代码: using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; usi ...

  4. 百度地图Canvas实现十万CAD数据秒级加载

    背景 前段时间工作室接到一个与地图相关的项目,我作为项目组成员主要负责地图方面的设计和开发.由于地图部分主要涉及的是前端页面的显示,作为一名Java后端的小白,第一次写了这么多HTML和JavaScr ...

  5. 关于python导入数据库excel数据时出现102, b"Incorrect syntax near '.15562'.DB-Lib error message 20018, severity 1的问题总结

    1.对于在使用python导入sqlsever时,出现102, b"Incorrect syntax near '.15562'.DB-Lib error message 20018, se ...

  6. 【 转】百度地图Canvas实现十万CAD数据秒级加载

    Github上看到: https://github.com/lcosmos/map-canvas 这个实现台风轨迹,这个数据量非常庞大,当时打开时,看到这么多数据加载很快,感到有点震惊,然后自己研究了 ...

  7. 循序渐进开发WinForm项目(5)--Excel数据的导入导出操作

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  8. PLSQL导入Excel数据方法

    1.把Excel文件另存为(文本文件(制表符分隔)(*.txt))   2.把新生成的student.txt文件导入到plsql   打开plsql连接到要导入的oracle数据库再打开Tools - ...

  9. php做EXCEL数据导出导入开发的一些小问题

    前两天刚刚做开发CRM系统项目,在做要做EXCEL导出导入功能,因为以前做.NET开发用的是NPOI,但可是没找到PHP版本的,所以就网搜找了个国外的开源PHPEXCEL , 一开始只是做了简单的导入 ...

随机推荐

  1. 【线段树】uoj#228. 基础数据结构练习题

    get到了标记永久化 sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧. 在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手.于是她的好朋友九条可怜酱给她出了一道题. 给出一 ...

  2. back to back

    back to back 传输,以前在AMBA bus中遇到过,FIFO设计中再次遇到. 查了资料大概意思是:直接传输,不依靠中介,连续多次传输.

  3. PAT Basic 1054

    1054 求平均值 本题的基本要求非常简单:给定 N 个实数,计算它们的平均值.但复杂的是有些输入数据可能是非法的.一个“合法”的输入是 [−1000,1000] 区间内的实数,并且最多精确到小数点后 ...

  4. apk 解包 打包

    APK应用程序的解包.修改.编辑.汉化.打包及应用 前两讲主要讲玩机的最基本的知识,集中在如何刷机.本讲是进级的内容,来谈谈与apk应用程序有关的知识,内容包括akp文件的解包.打包.反编辑.解析.汉 ...

  5. 【01】markdown特殊说明

    [01]说明 Markdown 的目标是实现「易读易写」. 可读性,无论如何,都是最重要的.一份使用 Markdown 格式撰写的文件应该可以直接以纯文本发布,并且看起来不会像是由许多标签或是格式指令 ...

  6. 面试准备——redis

    https://blog.csdn.net/yangzhong0808/article/details/81196472 http://www.imooc.com/article/36399 http ...

  7. 从PHP5到PHP7的注意事项——PHP7全面删除Mysql扩展支持

    PHP删除了一些函数,其中是吓人的是两个: 1. ereg_* 系列的正则函数 2. mysql_* 系列的数据库连接函数 PHP7全面删除Mysql扩展支持,原本的mysql_*系列函数将在mysq ...

  8. numpy hstack()

    numpy.hstack(tup)[source] Stack arrays in sequence horizontally (column wise). Take a sequence of ar ...

  9. Git 常用命令整理(持续更新)

    #配置 git config --global user.name "Your Name" git config --global user.email "email@e ...

  10. iOS第三方网络图片加载- SDWebImage笔记(转)

    SDWebImage托管在github上.https://github.com/rs/SDWebImage 这个类库提供一个UIImageView类别以支持加载来自网络的远程图片.具有缓存管理.异步下 ...