2010-07-21 2 views
0

В моем приложении ASP.NET MVC я запускаю сразу несколько вставок, которые могут вставлять 10000 или более строк и обновлять несколько других. Этот процесс занимает много времени, но я не могу избежать вставки, потому что это именно то, что меня просили сделать. Прямо сейчас я запускаю Sql Server Profiler и для вставки этой группы строк требуется почти 20 минут. Как я могу улучшить производительность этого действия?Улучшение производительности для оператора вставки

(я использую Linq к Sql для вставки данных в базу данных.)

Это код метода делает вставки:

[AcceptVerbs(HttpVerbs.Post)] 
     public ActionResult SaveEvent(int id) 
     { 
      int eventID= 0; 
      var query = from q in context.InventoryGoods 
         where q.ParentId == id && q.Action.HasValue && q.ActionOn.HasValue == false 
         select q; 

      var stockType = from q in context.Inventory 
          where q.Id == id 
          select q.StockType; 

      if (query.Count() > 0) 
      { 
       foreach (var i in query) 
       { 
         switch (i.Action.Value) 
         { 
          case (int)InventoryGoodsActionEnum.AdjustLocation: 

           Guid guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "LO", 
             Lid = i.LidObtained, 
             Comments = "Inventário "+i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 



           break; 

          case (int)InventoryGoodsActionEnum.AdjustQuantity: 

           if (!i.QuantityObtained.HasValue) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now, 
              EventOn = DateTime.Now, 
              Type = "AQ", 
              Quantity = (short)(i.QuantityExpected * -1), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 

           } 
           else if ((i.QuantityObtained - (i.QuantityExpected.HasValue ? i.QuantityExpected : 0) != 0)) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now, 
              EventOn = DateTime.Now, 
              Type = "AQ", 
              Quantity = (short)(i.QuantityObtained.Value - (i.QuantityExpected.HasValue ? i.QuantityExpected : 0)), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 
           } 


           break; 

          case (int)InventoryGoodsActionEnum.AdjustQuantityLocation: 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "LO", 
             Lid = i.LidExpected, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 
           if (!i.QuantityObtained.HasValue) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityExpected * -1), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 

           } 
           else if ((i.QuantityObtained - (i.QuantityExpected.HasValue ? i.QuantityExpected : 0) != 0)) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityObtained.Value - (i.QuantityExpected.HasValue ? i.QuantityExpected : 0)), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 
           } 



           break; 

          case (int)InventoryGoodsActionEnum.AdjustStockType: 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "ST", 
             StockType = stockType.First().Value, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 


           break; 
          case (int)InventoryGoodsActionEnum.AdjustLocationStockType: 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "ST", 
             StockType = stockType.First().Value, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now.AddSeconds(1), 
             EventOn = DateTime.Now.AddSeconds(1), 
             Type = "LO", 
             Lid = i.LidExpected, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 



            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 



           break; 
          case (int)InventoryGoodsActionEnum.AdjustQuantityStockType: 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "ST", 
             StockType = stockType.First().Value, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 

           if (!i.QuantityObtained.HasValue) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityExpected * -1), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 

           } 
           else if ((i.QuantityObtained - i.QuantityExpected != 0)) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityObtained.Value - i.QuantityExpected), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 
           } 


           break; 
          case (int)InventoryGoodsActionEnum.AdjustQuantityLocationStockType: 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now, 
             EventOn = DateTime.Now, 
             Type = "ST", 
             StockType = stockType.First().Value, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 

            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 

           if (!i.QuantityObtained.HasValue) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityExpected * -1), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 

           } 
           else if ((i.QuantityObtained - i.QuantityExpected != 0)) 
           { 
            guid = Guid.NewGuid(); 

            using (var scope = new TransactionScope()) 
            { 
             GoodsEvent ge = new GoodsEvent() 
             { 
              Gid = i.Gid, 
              Guid = guid, 
              CreatedOn = DateTime.Now.AddSeconds(1), 
              EventOn = DateTime.Now.AddSeconds(1), 
              Type = "AQ", 
              Quantity = (short)(i.QuantityObtained.Value - i.QuantityExpected), 
              Comments = "Inventário " + i.ParentId, 
              UserId = GetUserId(), 
             }; 

             context.GoodsEvent.InsertOnSubmit(ge); 

             context.SubmitChanges(); 
             eventID = ge.Id; 
             Repository.SetActionOn(i.Id, eventID); 

             scope.Complete(); 
            } 
           } 

           guid = Guid.NewGuid(); 

           using (var scope = new TransactionScope()) 
           { 
            GoodsEvent ge = new GoodsEvent() 
            { 
             Gid = i.Gid, 
             Guid = guid, 
             CreatedOn = DateTime.Now.AddSeconds(2), 
             EventOn = DateTime.Now.AddSeconds(2), 
             Type = "LO", 
             Lid = i.LidExpected, 
             Comments = "Inventário " + i.ParentId, 
             UserId = GetUserId(), 
            }; 



            context.GoodsEvent.InsertOnSubmit(ge); 

            context.SubmitChanges(); 
            eventID = ge.Id; 
            Repository.SetActionOn(i.Id, eventID); 

            scope.Complete(); 
           } 




           break; 
        } 
       } 
      } 
      else 
      { 
       var lista = from q in context.InventoryGoods 
          where q.ParentId == id 
          select q; 

       Repository.EvaluateActions(lista.ToList()); 

       SaveEvent(id); 
      } 


      using (var scope = new TransactionScope()) 
      { 
       var thisInventory = from i in context.Inventory 
            where i.Id == id 
            select i; 

       thisInventory.First().State = (int)InventoryStateEnum.Verified; 

       context.SubmitChanges(); 

       scope.Complete(); 
      } 

      Status.Info(string.Format("Acções aplicadas com sucesso.")); 
      return RedirectToAction("Details", new { id }); 
     } 



public void SetActionOn(int id, int eventID) 
     { 
      var InventoryGoods = from i in context.InventoryGoods 
           where i.Id == id 
           select i; 



      using (var scope = new TransactionScope()) 
      { 
       InventoryGoods.First().ActionOn = DateTime.Now; 

       InventoryGoodsEvents ige = new InventoryGoodsEvents 
       { 
        EventId = eventID, 
        InventoryGood = InventoryGoods.First().Id, 
       }; 

       context.InventoryGoodsEvents.InsertOnSubmit(ige); 

       scope.Complete(); 
      } 
     } 
+0

Что происходит в этом методе: Repository.SetActionOn (i.Id, eventID); –

+0

Я отправлю его выше на моем основном сообщении. – Hallaghan

ответ

1

Остановить использование var so much.


Выполняет запрос дважды (см. В sqlprofiler).

if (query.Count() > 0) 
{ 
    foreach (var i in query) 

Используйте это, чтобы не запускать запрос несколько раз.

List<InventoryGoods> rows = query.ToList(); 

Ваш код имеет много повторов. Кроме того, вы пытаетесь сделать слишком мало в контексте в каждом представлении. Вы контролируете сферу действия без каких-либо причин.

foreach(InventoryGood i in rows) 
{ 
    InventoryGoodsEvent ige = new InventoryGoodsEvent() 
    //this will attach ige to the object graph tracked by context 
    // which is sufficient to insert ige when submitchanges is called. 
    ige.InventoryGood = i; 

    GoodsEvent ge = GetGoodsEvent(i); //all that conditional logic in there. 
    //this will attach ge to the object graph tracked by context 
    // which will both insert ge and update ige with ge's id when submitchanges is called. 
    ige.GoodsEvent = ge; 

    i.ActionOn = DateTime.Now; 
    //to submit each row, uncomment this. 
    //context.SubmitChanges(); 
} 
//to submit all rows at once, use this. 
context.SubmitChanges(); 

Если InventoryGoodEgvents не те реляционные свойства, перейти в дизайнер и добавить ассоциации для их создания.

После того, как у вас есть такой код, вы можете решить, что хорошего количества изменений сделать в одной транзакции. Мне нравится вставлять ~ 100 записей за транзакцию. Если вы используете 1 запись за транзакцию, есть большие накладные расходы на создание каждой транзакции. Если вы используете 1 000 000 строк на транзакцию, есть высокая накладная стоимость долговременной транзакции.

Этот материал трудно узнать, но держитесь за него.

Еще одна вещь: Массовая вставка не будет работать с этой таблицей многих ко многим.

+2

Нет ничего плохого в том, как здесь использовался var. Это связано с запросами Linq, которые все знают, возвращают IEnumerable или все, что вы выбрали, и он также используется в сочетании с новым ключевым словом - который намного менее избыточен, чем смешной «TransactionScope scope = new TransactionScope();». Var можно переоценить, но здесь это было, безусловно, подходящим. mattmc3

+0

Существуют определенные объявления, в которых требуется var. Использование var помимо этого является вопросом стиля (как вы говорите, субъективным). Ваш выбранный случай сохраняет всего 13 символов. Возможно, если линия не была оптимизирована для длины, она не использовалась бы столько раз, в конце концов, чтобы сохранить много других символов. В случае назначения результата синтаксиса запроса понимания, как вы говорите «или что-то еще». «Или что-то» это проблема. –

0

Что DAL вы используете EF, L2S, ADO.net или что-то еще? Вставка не должна занимать много времени. Вы можете вставить их в локальный кеш и внести изменения позже.

+0

Мы используем ADO.NET. – Hallaghan

+0

После попытки вставить 8500 строк, для завершения операции потребовалось 30 минут. – Hallaghan

+0

ОК, понял.Посмотрите на этот шаблон ниже: context.GoodsEvent.InsertOnSubmit (ge); context.SubmitChanges(); Просто переместите «context.SubmitChanges();» утверждение вне сферы действия даже из цикла foreach. SubmitChanges - это трудоемкая операция, синхронизация локального кеша и удаленного состояния данных Sql DB. Если вы вставляете 8500 строк, он будет синхронизироваться 8500 раз вправо? Но после перемещения SubmitChanges() из цикла, он только синхронизируется один раз. – Wyvern

1

Linq-to-sql действительно не был предназначен для вставки этого количества записей в базу данных в одной партии. Он сделает это insert заявление от insert заявление, которое очень медленно. Я бы рекомендовал, чтобы в любом месте, где вы знаете, вам нужно будет поддерживать это множество вставок, в которых вы используете объект SqlBulkCopy вместо ваших классов Linq-to-sql. Вы даже можете использовать те же классы L2S, если они вам нужны для проверки объектов, но затем просто выгрузите их в DataTable в 1000 строк строк и дайте SqlBulkCopy делать свои фактические вставки. Вы могли бы даже google L2S и SqlBulkCopy и посмотреть, что там есть в отношении методов расширения или другой интеграции. Вы не первый, кто столкнулся с этой проблемой.

+0

+1 для 'SqlBulkCopy' – abatishchev

+0

Вы просматривали код моего метода выше? Как вы думаете, я мог бы улучшить его, чтобы сделать его быстрее? Мне интересно, могу ли я запустить эти «context.SubmitChanges();» просто в конце моего переключателя или если мне нужно запустить их, как только они запущены. Как вы думаете? – Hallaghan

+1

Этого не было, когда я отправил свой ответ, но на первый взгляд много дублирования кода. Это может созревать для рефакторинга ** перед ** оптимизацией производительности вставки. – mattmc3

Смежные вопросы