数据导入

1、功能介绍

该功能是 增、删、改的一个升级版本,可以说是非常有实用价值,如果你们把SqlSugar的增、删、改都学习完了,可以学习一下该功能,主要适用于:

1、大数据通用保存

2、大数据EXCEL数据导入

3、大数据数据验证

4、可以编辑的表格 保存

 1.1 一图看懂原理

将数据进行分组,然后可以针对分组进行批量操作(高性能

image.png

一条记录同时Update=true和 Insert=true 那就看优先级哪个高

Ignore> Other(预留)>Error>Delete> Update> Insert

总结:哪个为true就进哪个分组,如果2个true就看优先级,分好组之后可以操作你分组的数据,也可以不操作

 1.2学会调试分组

进了哪个分组,我们可以通过断点进行调试

性能优化:图中的 Insert NotAny可以改成 it=>true (因为Insert优先级最低不是其他分类就肯定是插入)

image.png

分组后去核对你的数据,是不是正确

TotalList=InsertList+UpdateList+ErrorList+IgnoreList+OtherList+DeleteList

比如 InsertList 有一条值那么 x.AsInsertable.Execommand()就插入1条

2.完整用例(增、删、改)

 2.1、测试数据

我们装备4条测试数据

List<UinitBlukTable> list2 = new List<UinitBlukTable>();
list2.Add(new UinitBlukTable() { Id = 1, Name = "a",Sex=1 ,Create = DateTime.Now });
list2.Add(new UinitBlukTable() { Id = 2, Name = "a",Sex=0,Create = DateTime.Now });
list2.Add(new UinitBlukTable() { Id = 3, Name = "a",Sex=0,Create = DateTime.Now.AddYears(-2) });
list2.Add(new UinitBlukTable() { Id = 4, Name ="",Sex=0,Create = DateTime.Now.AddYears(-2) });

 2.2、编写代码

将错误数据、可插入数据、可更新数据等进行分类  

var sexList=查询字典Sex;//像这种字典或者多表验证一次查出来(不要超过1万条为佳想办法太多想办法拆分)
 
var x = Db.Storageable(list2) //不要超过2万,超过2万用页 (标题5)
      //性能Ok:内存过滤不会循环读库
      .SplitError(it =>!sexList.Any(z=>z.Name==it.Item.Sex),"性别不存在字典中")
      //性能差: 适合非批量的验证操作
      .SplitError(it => db.Queryable<UinitBlukTable>().Any(s=>s.Id==it.Item.Phone),"手机已存在")
      //性能OK:无操作数据库,只内存验证非空
      .SplitError(it => string.IsNullOrEmpty(it.Item.Name), "名称不能为空")
      .SplitError(it => it.Item.Create<DateTime.Now.AddYears(-1),"不是今年的数据")
      .SplitError(it => string.IsNullOrEmpty(it.Item.Name), "名称不能为空")
      .SplitDelete(it =>it.Item.Create<DateTime.Now.AddYears(-10))//删除10年前数据
      .SplitInsert(it => true )//其余插入(因为插入优先级最低不满其他条件就是插入)
      .SplitUpdate(it => it.Any())//数据库存在更新 根据主键
      .ToStorage();
//输出统计                      
Console.WriteLine(" 插入 {0} 更新{1} 错误数据{2} 不计算数据{3} 删除数据{4},总共{5}" ,
                   x.InsertList.Count,
                   x.UpdateList.Count,
                   x.ErrorList.Count,
                   x.IgnoreList.Count,
                   x.DeleteList.Count,
                   x.TotalList.Count
//输出错误信息               ); 
foreach (var item in x.ErrorList)
{
   Console.WriteLine("id等于"+item.Item.Id+" : "+item.StorageMessage);
}               
                
 x.AsInsertable.ExecuteCommand(); //执行插入
 x.AsUpdateable.ExecuteCommand(); //执行更新
 x.AsDeleteable.ExecuteCommand(); //执行删除  

执行代码输出结果:

统计:

错误信息:

   

我们可以看到输出id3和id4是错误的,并且可以输出具体的错误明细

3、相互之间的优先级

Ignore> Other>Error>Delete> Update> Insert, 就是说 同时满足  Ignore和 Delete那么这条数据将会属于Ignore,

当前的顺序是非常科学合理的,所以如果遇到几个都成立的时候可以看一下优先级关系

 var x=Db.Storageable(list2)
                .SplitInsert(it => true)
                .SplitUpdate(it => it.Any()) //SplitInsert和SplitUpdate可以用Saveble替换
                .SplitDelete(it=>it.Item.Id>10)
                .SplitIgnore(it=>it.Item.Id==1)
                .SplitError(it => it.Item.Id == 3,"id不能等于3")
                .SplitError(it => it.Item.Id == 4, "id不能等于4")
                .SplitError(it => it.Item.Id == 5, "id不能等于5")
                .SplitError(it => it.Item.Name==null, "name不能等于")
                .WhereColumns(it=> new { it.Id })
                .ToStorage();

上面虽然Insert是true,只要进Error或者Ignore或者Update都轮不到Insert

4、As功能详解

我们可以将分组后的数据直接进行数据操作转换,并且转换后的功能是100% 增、删、改 功能

AsInsertable 

AsUpdateable

AsDeleteable

x.AsInsertable.IgnoreColumns(it => new { it.Name, it.TestId }).ExecuteReturnIdentity();

5、性能优化

数据据量超过2万最好进行分页处理,性能会大大提升

//分页能起到不错的性能提升
db.Utilities.PageEach(list, 2000 ,pageList=> {
     
     
       var x = Db.Storageable(pageList)
              .SplitUpdate(it => it.Any())//数据库存在更新 根据主键
              .SplitInsert(it => true )//其余插入
              .ToStorage();//将数据进行分组
    x.BulkCopy();
    x.BulkUpdate(); //5.0.4.6
    
});

//如果这么写出现CPU过高那么,就需要检查条件字段
//varchar设置50以下千万不能用大字段,如果是非重复数据有索引最佳
//数据库可以把varchar换成navarchar测试一下2者性能差距,选取最优的类型

6、验证名称是否重复(有难度

我们需要对Any的原理深一步理解,把存在数据库的数据全部查询出来然后在进行.Any筛选

it.Any()//等于下面
it.Any(y=>y.id==it.Item.Id)//等于下面
(select * from table where id in list).ToList().Any(y=>y.id==it.Item.Id)//多次Any也只会读取一次数所库
//所以Any只是在现有存在的主键里面过滤,并没有全库处理

错误写法:

 var x=Db.Storageable(saveObject)
    .SplitError(i => i.Any(it => it.Name== i.Item.Name), "名称已存在")//编辑验证
    .SplitInsert(i=>true)//其余插入
    .ToStorage();
  //因为it只是根据主键存在数据, 主键不存在的数据无法过滤

正确写法:

 var x=db.Storageable(list)
                .SplitError(it => it.Any(), "名称已存在")
                .SplitInsert(it => true)
                .WhereColumns(it => it.Name)//这里用name作为数据库查找条件
                .ToStorage()
    x.AsInsertable.ExecuteCommand();//插入可插入部分
    if(x.ErrorList.Any())//输出所有错误部分
     {
       foreach (var item in x.ErrorList)
      {
        Console.WriteLine(item.StorageMessage);
      }
     }

7、DataTable 高级保存

上面都是实体操作,新版本支持了DataTable

    var x =
        db.Storageable(dt)
        .SplitUpdate(it=>it.Any())
        .SplitInsert(it => true)
        .SplitDelete(it=>Convert.ToInt32( it["id"])==100)
        .WhereColumns("id").ToStorage();
        
    //分组优先级是 删除>更新>插入    
    x.AsDeleteable.ExecuteCommand();
    x.AsInsertable.IgnoreColumns("id").ExecuteCommand();//如果是自增要添加IgnoreColumns
     x.AsUpdateable.ExecuteCommand();

8、时间类型问题

新功能:5.0.9.3-preview05 时间格式化防止时间精度引起的失效 ,针对多元化时间格式进行处理

 var x=Db.Storageable(list2) 
 .SplitUpdate(it=>it.Any())
  .SplitInsert(it => true)
 .WhereColumns(it=>it.CreateTime, date=>date.ToString("yyyy-MM-dd HH:mm:ss.fff"))
 .ToStorage();
  
   x.AsInsertable.ExecuteCommand(); //执行插入
   x.AsUpdateable.ExecuteCommand(); //执行更新 
  
 //完美解决时间匹配不到问题


关闭
果糖网