2010-07-21 5 views
0

내 ASP.NET MVC 애플리케이션에서 한 번에 몇 개의 삽입을 실행하여 10000 개 이상의 행을 삽입하고 몇 가지 다른 것들을 업데이트 할 수 있습니다. 이 과정은 오랜 시간이 걸리지 만 삽입 물을 피할 수는 없기 때문에 그것이 정확하게 요구되었습니다. 지금 SQL Server 프로필러를 실행 중이며이 행을 삽입하는 데 약 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를 너무 많이 사용하지 마세요.


이렇게하면 쿼리가 두 번 실행됩니다 (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

OK, 알겠습니다.아래의 패턴을보십시오 : context.GoodsEvent.InsertOnSubmit (ge); context.SubmitChanges(); "context.SubmitChanges();"로 이동하십시오. foreach 루프에서조차도 범위를 벗어난 진술. SubmitChanges는 시간이 많이 소요되는 작업으로 로컬 캐시와 원격 SQL DB 데이터 상태를 동기화합니다. 8500 라인을 삽입하면 8500 번 올바르게 동기화됩니까? 그러나 SubmitChanges()를 루프 밖으로 이동 한 후에는 한 번만 동기화됩니다. – Wyvern

1

Linq-to-SQL은 많은 레코드를 하나의 배치로 데이터베이스에 삽입하도록 설계되지 않았습니다. 정말 느린 진술 insertinsert에 의해 성명을 그것을 할 것입니다. 나는 당신이 Linq-to-SQL 클래스 대신에 SqlBulkCopy 오브젝트를 사용하는 많은 삽입을 지원해야한다는 것을 알고있는 곳이라면 어디에서라도 추천 할 것입니다. 객체 밸리데이션을 위해 필요하다면 같은 L2S 클래스를 사용할 수도 있지만, 그 다음에는 1000 행 청크 인 DataTable에 덤프하고 SqlBulkCopy이 실제 삽입을하도록하십시오. 당신은 구글 L2S와 SqlBulkCopy조차 할 수 있고 확장 방법이나 다른 통합과 관련하여 무엇이 있는지 볼 수 있습니다. 당신은이 문제를 다루는 첫 번째 사람이 아닙니다.

+0

+1에 대한 'SqlBulkCopy' – abatishchev

+0

위의 내 방법의 코드를 보셨나요? 내가 더 빠르게 만들 수 있다고 생각하니? 그 "context.SubmitChanges();를 실행할 수 있는지 궁금하네요." 내 스위치가 끝나거나 지금 실행 한대로 실행해야하는 경우. 어떻게 생각해? – Hallaghan

+1

내 대답을 올렸을 때 거기에 없었지만 언뜻보기에는 코드 중복이 많이있었습니다. 인서트 성능을 최적화하기 전에 ** 리팩터링에 익숙 할 수도 있습니다 **. – mattmc3