zoukankan      html  css  js  c++  java
  • 关于EF中批量添加的个人探索

    实际的测试代码和数据记录,还有最终的总结都在下面:

            /// <summary>
            /// 这种做法,不用了说了,每次遍历都会打开一次db链接,然后执行insert操作;
            /// </summary>
            static void CreateBluckInsertData0()
            {
                using (var context = new SiteDbContext())
                {
                    List<Role> list = new List<Role>();
                    var count = 1000;
                    for (int i = 0; i < count; i++)
                    {
                        var entity = new Role()
                        {
                            RoleName = "普通员工" + i
                        };
                        context.Roles.Add(entity);
                        context.SaveChanges();
                    }
    
                }
            }
    
            /// <summary>
            /// 初看,觉得,这样做( context.SaveChanges()方在for循环外面)挺好的,
            /// 实际跟踪slq发现,还是执行了1000的插入操作,只不过没有在for循环里面;
            /// 而是在我们的for循环外;总结:不可取;
            /// </summary>
            static void CreateBluckInsertData1()
            {
                using (var context = new SiteDbContext())
                {
                    List<Role> list = new List<Role>();
                    var count = 1000;
                    for (int i = 0; i < count; i++)
                    {
                        var entity = new Role()
                        {
                            RoleName = "普通员工" + i
                        };
                        context.Roles.Add(entity);
                    }
                    context.SaveChanges();//会增加与数据库的交互次数
                    //EF的一个上下文在提交时会打开一个数据连接,然后把转换成的SQL语句一条一条的发到数据库端,然后去提交
                }
            }
    
            /// <summary>
            /// 拼接字符串;组装后一次性操作;确定,传递的字符数量就会很多;网络压力增加;但不管怎样都比上面两张好;
            /// </summary>
            static void CreateBluckInsertData2()
            {
                Stopwatch watch = Stopwatch.StartNew();
                var count = 92000;
                using (var context = new SiteDbContext())
                {
                    var bluckString = new StringBuilder();
                    for (int i = 0; i < count; i++)
                    {
    
                        bluckString.Append("INSERT INTO ROLES(RoleName) VALUES('");
                        bluckString.Append("WORKER");
                        bluckString.Append(i);
                        bluckString.Append("');");
                    }
    
                    var result = bluckString.ToString();
                    Console.WriteLine(string.Format("拼接字符串花费的时间:{0} milliseconds.",watch.ElapsedMilliseconds));
                    //然后这里再来一次性批量的进行插入操作;
                    watch.Restart();
                    context.Database.ExecuteSqlCommand(result);
                    //这样做的好处就是,可以一次性,全部插入,缺点就是;发送大量的insert 文本信息;
                    //1000 customers are created, cost 1777 milliseconds.
                    //5000 customers are created, cost 1906 milliseconds.
                    //9000 customers are created, cost 2354 milliseconds.
                    //9000 customers are created, cost 2023 milliseconds.
                    //这样的计算比较草率;
                }
                watch.Stop();
                Console.WriteLine(string.Format("{0} customers are created, cost {1} milliseconds.", count.ToString(), watch.ElapsedMilliseconds));
    
                //结果:拼接字符串花费的时间:97 milliseconds.
                //12000 customers are created, cost 2028 milliseconds.
    
                //拼接字符串花费的时间:99 milliseconds.
                //22000 customers are created, cost 2336 milliseconds.
    
                //拼接字符串花费的时间:118 milliseconds.
                //92000 customers are created, cost 6553 milliseconds.
            }
    
            /// <summary>
            /// 这里我们使用第三种方法;
            /// 网上提供的插件的方法;
            /// 你以为,插件的方法,就是单纯的封装上面的操作?太年轻了,俺都没监测到一条insert 语句;那么它是怎么做的呢?
            /// 
            /// </summary>
            static void CreateBluckInsertData3()
            {
                Stopwatch watch = Stopwatch.StartNew();
                var count = 92000;
                using (var context = new SiteDbContext())
                {
                    List<Role> list = new List<Role>();
                    for (int i = 0; i < count; i++)
                    {
                        var entity = new Role()
                        {
                            RoleName = "普通员工" + i
                        };
                        list.Add(entity);
                        //context.Roles.Add(entity); //fuck stupid;
                    }
                    Console.WriteLine(string.Format("拼接对象花费的时间:{0} milliseconds.", watch.ElapsedMilliseconds));
                    //然后这里再来一次性批量的进行插入操作;
                    watch.Restart();
                    context.BulkInsert(list);
                    context.BulkSaveChanges();
                }
                watch.Stop();
                Console.WriteLine(string.Format("{0} customers are created, cost {1} milliseconds.", count.ToString(), watch.ElapsedMilliseconds));
                //1000 customers are created, cost 2865 milliseconds
                //5000 customers are created, cost 18207 milliseconds.
                //9000 customers are created, cost 51134 milliseconds. (发现是方法用错了,窝草).不要把 context.Roles.Add(entity); 添加在for循环中;
                //然后结果是这样的:9000 customers are created, cost 2320 milliseconds.
                //效率明显比上面的方法提高了很多;
                //由此可见,我们的批量,效果操作,并不由之前那种方法高呢;
    
                //拼接对象花费的时间:102 milliseconds.
                //12000 customers are created, cost 2238 milliseconds.
    
                //拼接对象花费的时间:101 milliseconds.    
                //22000 customers are created, cost 2258 milliseconds.
    
                ///拼接对象花费的时间:123 milliseconds.
               // 92000 customers are created, cost 3054 milliseconds.
    
                //这种方式的优势就不断体现出来了;
    
    
               //总结,凭借,字段串额效果,要比拼接对象集合的效率要高一些;
               //然后,就是我们的
    
            }
    
    
            //可能涉及到一些批量数据迁移的时候;
    
            ///总结:
            public void Info()
            {
                //凭借字段串的效率比拼接对象List的效率要高一些;
    
                //方式2的缺点在于,要传送大量的sql语句到我们的db中去执行,
    
                //方式3的实现方式和方式一有着本质的区别;是通过;
    
                //是数据小于五万条的时候,方式2的效率高,随着数据量的增加;方式3的优势就体现出来了
    
                //如果实际的开发中遇到大数据的批量操作;建议还是是用插件方式,就是我们的的方式3;
    
                //ps 操作中犯了一个错,是list.add(entity) 而不是 context.Roles.Add(entity); //fuck stupid;
    
    
            }
    
    
            static void Main(string[] args)
            {
    
                HibernatingRhinos.Profiler.Appender.EntityFramework.EntityFrameworkProfiler.Initialize();
    
                //CreateBluckInsertData1();
                //CreateDB();
                //CreateBluckInsertData3();
                // CreateBluckInsertData2();
    
                Console.ReadLine();
    
            }

    当然,这里还有我们的另外一种做法;

    SqlBulkCopy 接口描述

    Microsoft SQL Server 提供一个称为 bcp 的流行的命令提示符实用工具,用于将数据从一个表移动到另一个表(表既可以在同一个服务器上,也可以在不同服务器上)。 SqlBulkCopy 类允许编写提供类似功能的托管代码解决方案

    似乎这种效率更高一些(不过,俺没有去测)

    阅读资料后,发现,z的扩展插件使用的就是我们的sqlbulkcopy接口滴呀;

    大致的流程如下:

    • 在SQL Server中创建一张临时表;
    • 使用.NET SqlBulkCopy将数据批量插入临时表;
    • 在临时表和目标表之间执行一条SQL语句;
    • 从SQL Server删除临时表。

    参考文献:

    http://www.cnblogs.com/gaochundong/p/entity_framework_bulk_insert_extension.html 

    这里有一偏使用心得:

    https://www.cnblogs.com/mobydick/archive/2011/08/28/2155983.html

    还有这个:

    https://www.cnblogs.com/zfanlong1314/archive/2013/02/05/2892998.html

  • 相关阅读:
    跳跃表原理
    查看Oracle操作历史的试图
    【概念】为什么有时全表扫描比通过索引扫描效率更高
    oracle驱动表以及如何确定驱动表
    SpringBoot学习(三)-----配置Bean
    leetcode 面试题58
    leetcode 1365 有多少小于当前数字的数字
    leetcode 1342 将数字变成 0 的操作次数
    leetcode1313 解压缩编码列表
    leetcode 1071 字符串的最大公因子
  • 原文地址:https://www.cnblogs.com/mc67/p/8011432.html
Copyright © 2011-2022 走看看