本文将采用NPOI插件来读取execl文件里的数据,将数据加载到内存中的DataTable中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 ///2 /// 将Excel转换为DataTable 3 /// 4 /// 5 /// 6 ///7 public static System.Data.DataTable GetExcelDataTable(string extension, System.IO.Stream stream) { 8 NPOI.SS.UserModel.IWorkbook workBook; 9 if (extension == ".xls")10 workBook = new NPOI.HSSF.UserModel.HSSFWorkbook(stream);11 else if (extension == ".xlsx")12 workBook = new NPOI.XSSF.UserModel.XSSFWorkbook(stream);13 else14 throw new Exception("文件格式出错!");15 var sheet = workBook.GetSheetAt(0);16 var row = sheet.GetRow(0);17 System.Data.DataTable dt = new System.Data.DataTable(sheet.SheetName);18 foreach (var i in row) {19 var name = i.StringCellValue;20 if (string.IsNullOrEmpty(name))21 break;22 dt.Columns.Add(name);23 }24 dt.PrimaryKey = new DataColumn[1] { dt.Columns[0] };25 var rowCount = sheet.LastRowNum + 1;26 for (var i = 1; i < rowCount; ++i) {27 row = sheet.GetRow(i);28 string[] cells = new string[dt.Columns.Count];29 for (var j = 0; j < cells.Length; ++j) {30 var cell = row.GetCell(j);31 if (cell != null)32 cells[j] = cell.ToString();33 }34 if (dt.Rows.Find(cells[0]) == null) {35 dt.Rows.Add(cells);36 }37 }38 return dt;39 }
上面的方法中,把execl表格第一列数据设为DataTable的主键,同时使用dt.Rows.Find()方法去除重复项,主要是为了避免后面合并数据发生意外
加载到内存中的DataTable之后,在业务层处理一下,把execl文件里的第一行数据(即DataTable中的列名)修改成后面创建的临时表的列名一一对应
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 public static async Task BatchImport(System.Data.DataTable dt) { 2 dt.Columns["商品id"].ColumnName = "GOODSID"; 3 dt.Columns["商品名称"].ColumnName = "GOODSNAME"; 4 dt.Columns["商品主图"].ColumnName = "GOODSMAINIMG"; 5 dt.Columns["商品详情页链接地址"].ColumnName = "GOODSDETAIL"; 6 dt.Columns["商品一级类目"].ColumnName = "GOODSLEVEL"; 7 dt.Columns["推广链接"].ColumnName = "SHORTLINK2"; 8 dt.Columns["商品价格(单位:元)"].ColumnName = "GOODSPRICE"; 9 dt.Columns["商品月销量"].ColumnName = "GOODSMONTHSALE";10 dt.Columns["收入比率(%)"].ColumnName = "GOODSINCOME";11 dt.Columns["佣金"].ColumnName = "GOODSCOMMISSION";12 dt.Columns["卖家名称"].ColumnName = "SELLER";13 dt.Columns["卖家id"].ColumnName = "SELLERID";14 dt.Columns["店铺名称"].ColumnName = "STORENAME";15 dt.Columns["平台类型"].ColumnName = "PLATFORMTYPE";16 dt.Columns["优惠券id"].ColumnName = "COUPONID";17 dt.Columns["优惠券总量"].ColumnName = "COUPONTOTAL";18 dt.Columns["优惠券剩余量"].ColumnName = "COUPONSURPLUS";19 dt.Columns["优惠券面额"].ColumnName = "COUPONCONTENT";20 dt.Columns["优惠券开始时间"].ColumnName = "CONPONSTRATETIME";21 dt.Columns["优惠券结束时间"].ColumnName = "COUPONENDTIME";22 dt.Columns["优惠券链接"].ColumnName = "COUPONSHORTLINK";23 dt.Columns["商品优惠券推广链接"].ColumnName = "COUPONLINK";24 using (var con =await mssql.tbk.GetConnection()) { //一下执行的方法会放后面25 await mssql.tbk.CreateTmepTable(con);//创建临时表26 await mssql.tbk.BatchImport("#TMD", dt,con);//将DataTable中的数据导入到临时表(#TMD)27 await mssql.tbk.Combine(con);28 await mssql.tbk.DropTempTable(con);29 } 30 }
下面开始创建临时表,临时表的名字命名必须以#开头,结构要和execl文件的一模一样,字段不能多也不能少,否则导入数据时某些数据丢失
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 ///2 /// 创建临时表3 /// 4 /// 5 ///6 public static async Task CreateTmepTable(sqlClient.SqlConnection con) {7 await con.ExecuteNonQueryAsync("CREATE TABLE #TMD([GOODSID] BIGINT NOT NULL PRIMARY KEY,[GOODSNAME][varchar](250) NOT NULL,[GOODSMAINIMG][varchar](1024) NOT NULL,[GOODSDETAIL][varchar](1024) NOT NULL,[GOODSLEVEL][varchar](50) NOT NULL,[SHORTLINK2][varchar](1024) NOT NULL,[GOODSPRICE][varchar](100) NOT NULL,[GOODSMONTHSALE][varchar](100) NOT NULL,[GOODSINCOME][varchar](100) NOT NULL,[GOODSCOMMISSION][varchar](100) NOT NULL,[SELLER][varchar](100) NOT NULL,[SELLERID][varchar](100) NOT NULL,[STORENAME][varchar](100) NOT NULL,[PLATFORMTYPE][varchar](50) NOT NULL,[COUPONID][varchar](100) NOT NULL,[COUPONTOTAL][varchar](100) NOT NULL,[COUPONSURPLUS][varchar](100) NOT NULL,[COUPONCONTENT][varchar](100) NOT NULL,[CONPONSTRATETIME][varchar](100) NOT NULL,[COUPONENDTIME][varchar](100) NOT NULL,[COUPONSHORTLINK][varchar](1024) NOT NULL,[COUPONLINK][varchar](1024) NOT NULL)");8 }
成功创建临时表之后,开始将DataTable数据导入临时表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 ///2 /// 将execl数据批量导入临时表3 /// 4 /// 5 /// 6 ///7 public static async Task BatchImport(string tableName,System.Data.DataTable dt, sqlClient.SqlConnection con) {8 await con.InsertBulkCopyAsync(tableName,dt,System.Data.SqlClient.SqlBulkCopyOptions.Default);9 }
重点来了,重点来了,重点来了,如何将临时表的数据写到数据库主表(主表结构与临时表结构可相同可不相同)里呢?请看代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 ///2 /// 合并临时表数据到数据库主表3 /// 4 /// 5 ///6 public static async Task Combine(sqlClient.SqlConnection con) {7 await con.ExecuteNonQueryAsync("MERGE INTO TMD ts USING #TMD t ON ts.GOODSID=t.GOODSID WHEN MATCHED AND ts.GOODSID=t.GOODSID THEN UPDATE SET ts.GOODSNAME = t.GOODSNAME, ts.GOODSMAINIMG = t.GOODSMAINIMG, ts.GOODSDETAIL = t.GOODSDETAIL, ts.GOODSLEVEL = t.GOODSLEVEL, ts.SHORTLINK2 = t.SHORTLINK2, ts.GOODSPRICE = t.GOODSPRICE, ts.GOODSMONTHSALE = t.GOODSMONTHSALE, ts.GOODSINCOME = t.GOODSINCOME, ts.GOODSCOMMISSION = t.GOODSCOMMISSION, ts.SELLER = t.SELLER, ts.SELLERID = t.SELLERID, ts.STORENAME = t.STORENAME, ts.PLATFORMTYPE = t.PLATFORMTYPE, ts.COUPONID = t.COUPONID, ts.COUPONTOTAL = t.COUPONTOTAL, ts.COUPONSURPLUS = t.COUPONSURPLUS, ts.COUPONCONTENT = t.COUPONCONTENT, ts.CONPONSTRATETIME = t.CONPONSTRATETIME, ts.COUPONENDTIME = t.COUPONENDTIME, ts.COUPONSHORTLINK = t.COUPONSHORTLINK, ts.COUPONLINK = t.COUPONLINK WHEN NOT MATCHED THEN INSERT VALUES(t.GOODSID, t.GOODSNAME, t.GOODSMAINIMG, t.GOODSDETAIL, t.GOODSLEVEL, t.SHORTLINK2, t.GOODSPRICE, t.GOODSMONTHSALE, t.GOODSINCOME, t.GOODSCOMMISSION, t.SELLER, t.SELLERID, t.STORENAME, t.PLATFORMTYPE, t.COUPONID, t.COUPONTOTAL, t.COUPONSURPLUS, t.COUPONCONTENT, t.CONPONSTRATETIME, t.COUPONENDTIME, t.COUPONSHORTLINK, t.COUPONLINK,0);");8 }
上面方法中使用了merge into和using,Merge和using搭配用于特别是BI上数据统计和分析上 比如 要求子表中没有的数据那么父表中就要删除对应的数据 保证子表和父表的数据对应 如果按照常规的做法是 跑个作业 然后通过游标/表值函数/临时表等等循环的获取数据然后更新父表 这样是很浪费效率的 这时Merge派上用场了
merge的语法:
merge into 主表 T
using #临时表 D on T.关联字段=D.关联字段when matched then update set T.字段=D.字段。。。。。。。。。。。when not matched --为not matched时 不能为update(没有匹配成功 当然不能update了) then insert (D字段。。。)values(D.字段);想了解更详情请自行查找
合并完数据之后删除临时表(临时表会在数据库连接断开时清除临时表,这一步也可省)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 ///2 /// 删除临时表3 /// 4 /// 5 ///6 public static async Task DropTempTable(sqlClient.SqlConnection con) {7 await con.ExecuteNonQueryAsync("drop table #TAOBAO_SELECTED");8 }
写到这里基本搞定批量导入数据的问题了,如果你有更好的办法实现几万行的execl文件导入数据库,请留言,指导指导下。