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. (29)zabbix执行远程命令

    概述 监控,有的人只把他当做报警使用,出现问题之后打开跑回家打开电脑,巴拉巴拉的处理掉,大多数时候都是一些小问题,为何不让zabbix帮你把这些事情处理掉呢?和朋友具体,收到xx硬盘空间慢了.xx服务 ...

  2. Windows 10 Mac 为Vs Code配置C/C++环境

    2019-06-10 更新: 加上Mac版本的Vscode配置文件 0.前言 实现效果:右键一键编译运行C/C++文件 Vs code的代码效果很好看,也很轻量,所以想为Vs Code配置C/C++环 ...

  3. modelsim安装调试

    modelsim,debug:“unable to checkout a viewer license necessary for use of the modelsim graphical user ...

  4. linux kernel如何处理大端小端字节序

    (转)http://blog.csdn.net/skyflying2012/article/details/43771179 最近在做将kernel由小端处理器(arm)向大端处理器(ppc)的移植的 ...

  5. tomcat 下catalina.out 日志乱码问题处理

    问题: 项目部署到Linux服务器之后,控制台 catalina.out 文件输出的中文为乱码: 解决办法: 方法一:修改tomcat下的模板编码 bin/catalina.sh 文件添加如下配置:J ...

  6. Knockout v3.4.0 中文版教程-4-通过监控数组工作

    2.通过监控数组工作 1. 监控数组 如果你想检测或者响应一个对象的改变,你用observables.如果你想检测和响应一个集合的改变,使用observableArray.这个在很多情况下都非常有用, ...

  7. Knockout v3.4.0 中文版教程-2-监控-通过监控创建视图模型(上)

    2. 监控 1.通过监控创建视图模型 1. 监控 Knockout是基于以下三个核心特性: 监控和依赖跟踪 声明式绑定 模板 在本节,你将第一次了解这三个特性,在这之前,我们先来了解以下MVVM模式和 ...

  8. 九度oj 题目1026:又一版 A+B

    题目描述: 输入两个不超过整型定义的非负10进制整数A和B(<=231-1),输出A+B的m (1 < m <10)进制数. 输入: 输入格式:测试输入包含若干测试用例.每个测试用例 ...

  9. 【Luogu】P1072Hankson的趣味题(gcd)

    这题真TM的趣味. 可以说我的动手能力还是不行,想到了算法却写不出来.以后说自己数论会GCD的时候只好虚了…… 我们首先这么想. x与a0的最大公约数为a1,那么我们把x/=a1,a0/=a1之后,x ...

  10. 蛋疼的SVG外部引用方式

    SVG在html页面中有两种引用方式: 1. 内联.就是直接将SVG图形写在html的svg标签中,比如: <html> <head></head> <bod ...