C\#데 이 터 를 Sqlserver 에 대량으로 삽입 하 는 네 가지 방식

이 편 에 서 는 Sqlserver 에 데 이 터 를 대량으로 삽입 하 는 것 을 설명 하 겠 습 니 다.
먼저 테스트 할 데이터베이스 와 표를 만 듭 니 다.데 이 터 를 더 빨리 삽입 할 수 있 도록 표 의 홈 키 는 GUID 를 사용 합 니 다.표 에 색인 을 만 들 지 않 았 습 니 다.GUID 는 반드시 자체 성장 보다 빠 를 것 입 니 다.GUID 알고리즘 을 만 드 는 데 걸 리 는 시간 은 데이터 시트 에서 기 록 된 ID 의 값 을 다시 조회 한 다음 에 1 연산 을 하 는 것 보다 적 을 것 입 니 다.색인 이 존재 할 경우 기록 을 삽입 할 때마다 색인 재 구축 을 하 는 것 은 성능 을 소모 하 는 일이 다.표 에 피 할 수 없 는 색인 이 존재 한다 면 색인 을 먼저 삭제 한 다음 에 대량으로 삽입 한 다음 에 색인 을 재 구축 하 는 방식 으로 효율 을 높 일 수 있 습 니 다.

create database CarSYS; 
go 
use CarSYS; 
go 
CREATE TABLE Product(
Id UNIQUEIDENTIFIER PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
Price DECIMAL(18,2) NOT NULL
)
우 리 는 SQL 발 을 통 해 원래 데 이 터 를 삽입 하 는데 다음 과 같은 네 가지 방식 을 흔히 볼 수 있다.
방식 1:하나씩 삽입 하고 성능 이 가장 떨 어 지 므 로 사용 하 는 것 을 권장 하지 않 습 니 다.

INSERT INTO Product(Id,Name,Price) VALUES(newid(),'  1 ',160);
INSERT INTO Product(Id,Name,Price) VALUES(newid(),'  2 ',260);
......
방식 2:insert bulk
문법 은 다음 과 같다.

BULK INSERT [ [ 'database_name'.][ 'owner' ].]{ 'table_name' FROM 'data_file' } 
 WITH ( 
  [ BATCHSIZE [ = batch_size ] ], 
  [ CHECK_CONSTRAINTS ],  
  [ CODEPAGE [ = 'ACP' | 'OEM' | 'RAW' | 'code_page' ] ], 
  [ DATAFILETYPE [ = 'char' | 'native'| 'widechar' | 'widenative' ] ],  
  [ FIELDTERMINATOR [ = 'field_terminator' ] ], 
  [ FIRSTROW [ = first_row ] ], 
  [ FIRE_TRIGGERS ], 
  [ FORMATFILE = 'format_file_path' ], 
  [ KEEPIDENTITY ], 
  [ KEEPNULLS ], 
  [ KILOBYTES_PER_BATCH [ = kilobytes_per_batch ] ], 
  [ LASTROW [ = last_row ] ], 
  [ MAXERRORS [ = max_errors ] ], 
  [ ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ], 
  [ ROWS_PER_BATCH [ = rows_per_batch ] ], 
  [ ROWTERMINATOR [ = 'row_terminator' ] ],  
  [ TABLOCK ], 
 )
관련 매개 변수 설명:

 BULK INSERT 
 [ database_name . [ schema_name ] . | schema_name . ] [ table_name | view_name ] 
 FROM 'data_file' 
 [ WITH 
 ( 
 [ [ , ] BATCHSIZE = batch_size ] --BATCHSIZE                         
 [ [ , ] CHECK_CONSTRAINTS ] --            ,                。    CHECK_CONSTRAINTS   ,    CHECK   FOREIGN KEY        ,                   。 
 [ [ , ] CODEPAGE = { 'ACP' | 'OEM' | 'RAW' | 'code_page' } ] --               
 [ [ , ] DATAFILETYPE = 
 { 'char' | 'native'| 'widechar' | 'widenative' } ] --   BULK INSERT                   。 
 [ [ , ] FIELDTERMINATOR = 'field_terminator' ] --          
 [ [ , ] FIRSTROW = first_row ] --            。                
 [ [ , ] FIRE_TRIGGERS ] --        
 [ [ , ] FORMATFILE = 'format_file_path' ] 
 [ [ , ] KEEPIDENTITY ] --                   
 [ [ , ] KEEPNULLS ] --                     ,              
 [ [ , ] KILOBYTES_PER_BATCH = kilobytes_per_batch ] 
 [ [ , ] LASTROW = last_row ] --              
 [ [ , ] MAXERRORS = max_errors ] --                  ,                。 
 [ [ , ] ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ] --               
 [ [ , ] ROWS_PER_BATCH = rows_per_batch ] 
 [ [ , ] ROWTERMINATOR = 'row_terminator' ] --         
 [ [ , ] TABLOCK ] --                      
 [ [ , ] ERRORFILE = 'file_name' ] --                 OLE DB        。 
 )]
방식 3:INSERT INTO xx select...

 INSERT INTO Product(Id,Name,Price)
 SELECT NEWID(),'  1 ',160 
 UNION ALL 
 SELECT NEWID(),'  2 ',180
 UNION ALL
......
방식 4:SQL 연결

INSERT INTO Product(Id,Name,Price) VALUES
(newid(),'  1 ',160)
,(newid(),'  2 ',260)
......
C\#에서 ADO.NET 을 통 해 대량 작업 을 실현 하 는 데 네 가지 대응 하 는 방식 이 존재 합 니 다.
방식 1:조목조목 삽입

#region    
 static void InsertOne()
 {
  Console.WriteLine("             ");
  Stopwatch sw = new Stopwatch();
  using (SqlConnection conn = new SqlConnection(StrConnMsg)) //using    Open Close   。
  {
  string sql = "INSERT INTO Product(Id,Name,Price) VALUES(newid(),@p,@d)";
  conn.Open();
  for (int i = 0; i < totalRow; i++)
  {
   using (SqlCommand cmd = new SqlCommand(sql, conn))
   {
   cmd.Parameters.AddWithValue("@p", "  " + i);
   cmd.Parameters.AddWithValue("@d", i);
   sw.Start();
   cmd.ExecuteNonQuery();
   Console.WriteLine(string.Format("      ,   {0}  ", sw.ElapsedMilliseconds));
   }
   if (i == getRow)
   {
   sw.Stop();
   break;
   }
  }
  }
  Console.WriteLine(string.Format("  {0}   , {4}       {1}  ,         {2}  ,{3}  ",
 totalRow, sw.ElapsedMilliseconds, ((sw.ElapsedMilliseconds / getRow) * totalRow), GetMinute((sw.ElapsedMilliseconds / getRow * totalRow)), getRow));
 }
 static int GetMinute(long l)
 {
  return (Int32)l / 60000;
 } 
 #endregion
실행 결 과 는 다음 과 같 습 니 다.

100 w 개의 기록 을 삽입 하 는 데 50 분 이 걸 릴 것 으로 예상 되 며,기록 을 삽입 할 때마다 약 3 밀리초 정도 걸 릴 것 으로 예상 된다.
방식 2:SqlBulk 사용

#region    
 static void InsertTwo()
 {
  Console.WriteLine("  Bulk       ");
  Stopwatch sw = new Stopwatch();
  DataTable dt = GetTableSchema(); 
  using (SqlConnection conn = new SqlConnection(StrConnMsg))
  {
  SqlBulkCopy bulkCopy = new SqlBulkCopy(conn);
  bulkCopy.DestinationTableName = "Product";
  bulkCopy.BatchSize = dt.Rows.Count;
  conn.Open();
  sw.Start();
  for (int i = 0; i < totalRow;i++ )
  {
   DataRow dr = dt.NewRow();
   dr[0] = Guid.NewGuid();
   dr[1] = string.Format("  ", i);
   dr[2] = (decimal)i;
   dt.Rows.Add(dr);
  }
   if (dt != null && dt.Rows.Count != 0)
   {
   bulkCopy.WriteToServer(dt);
   sw.Stop();
   }
   Console.WriteLine(string.Format("  {0}      {1}  ,{2}  ", totalRow, sw.ElapsedMilliseconds, GetMinute(sw.ElapsedMilliseconds)));
  }
 }
 static DataTable GetTableSchema()
 {
  DataTable dt = new DataTable();
  dt.Columns.AddRange(new DataColumn[] { 
 new DataColumn("Id",typeof(Guid)), 
 new DataColumn("Name",typeof(string)), 
 new DataColumn("Price",typeof(decimal))});
  return dt;
 }
 #endregion
실행 결 과 는 다음 과 같 습 니 다.

100 w 개의 기록 을 삽입 한 지 겨우 8s 가 넘 었 는데,매우 빠 르 지 않 습 니까?
Sqlserver Profiler 추적 을 열 면 다음 문장 이 실 행 됩 니 다.insert bulk Product ([Id] UniqueIdentifier, [NAME] VarChar(50) COLLATE Chinese_PRC_CI_AS, [Price] Decimal(18,2))방식 3:TVP(표 값 매개 변수)를 사용 하여 데 이 터 를 삽입 합 니 다.
sqlserver 2008 부터 TVP 를 지원 합 니 다.캐 시 시트 ProductTemp 를 만 들 고 다음 SQL 을 실행 합 니 다.

CREATE TYPE ProductTemp AS TABLE(
Id UNIQUEIDENTIFIER PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
Price DECIMAL(18,2) NOT NULL
)
실행 이 완료 되면 데이터베이스 CarSYS 아래 에 캐 시 시트 ProductTemp 가 한 장 더 있 는 것 을 발견 할 수 있 습 니 다.


100 w 개의 기록 을 삽입 하 는 데 총 11 초가 걸 린 것 을 알 수 있다.
방식 4:SQL 연결
이 방법 은 C\#에 제한 이 있어 서 한번에 1000 개 만 대량으로 삽입 할 수 있 기 때문에 세그먼트 별로 삽입 해 야 합 니 다.

#region    
 static void InsertFour()
 {
  Console.WriteLine("      SQL       ");
  Stopwatch sw = new Stopwatch();
  using (SqlConnection conn = new SqlConnection(StrConnMsg)) //using    Open Close   。
  {
  conn.Open();
  sw.Start();
  for (int j = 0; j < totalRow / getRow;j++ )
  {
   StringBuilder sb = new StringBuilder();
   sb.Append("INSERT INTO Product(Id,Name,Price) VALUES");
   using (SqlCommand cmd = new SqlCommand())
   { 
   for (int i = 0; i < getRow; i++)
   {
    sb.AppendFormat("(newid(),'  {0}',{0}),", j*i+i);
   }
   cmd.Connection = conn;
   cmd.CommandText = sb.ToString().TrimEnd(',');
   cmd.ExecuteNonQuery();
   }
  }
  sw.Stop();
  Console.WriteLine(string.Format("  {0}   ,   {1}  ",totalRow,sw.ElapsedMilliseconds));
  }
 }
 #endregion

실행 결 과 는 다음 과 같 습 니 다.

우 리 는 대략 10 분 이 걸 린 것 을 볼 수 있다.방식 1 을 바탕 으로 성능 이 크게 향상 되 었 지만 빠 르 지 않 은 것 이 분명 하 다.
요약:빅 데이터 의 대량 삽입 방식 1 과 방식 4 는 가능 한 한 사용 을 피하 고 방식 2 와 방식 3 은 모두 매우 효율 적 인 대량 삽입 데이터 방식 이다.모두 DataTable 을 구축 하 는 방식 으로 삽입 되 었 으 며,DataTable 은 메모리 에 존재 한 다 는 것 을 알 고 있 기 때문에 데이터 양 이 매우 많 고 메모리 에 한꺼번에 저장 할 수 없 을 정도 로 클 때 세그먼트 로 삽입 할 수 있 습 니 다.예 를 들 어 9 천만 개의 데 이 터 를 삽입 해 야 하 는데 9 단 으로 나 누 어 삽입 할 수 있 고 한 번 에 1 천만 개 를 삽입 할 수 있다.그리고 for 순환 에서 데이터 베 이 스 를 직접 조작 할 때 우 리 는 가능 한 한 피해 야 한다.모든 데이터 베 이 스 를 연결 하고 열 고 닫 는 데 시간 이 걸 립 니 다.C\#에 데이터베이스 연결 풀 이 존재 하지만 우리 가 using 또는 conn.close()를 사용 하여 연결 을 풀 때 데이터 베 이 스 를 진정 으로 닫 지 않 았 습 니 다.연결 을 휴면 과 유사 하 게 존재 하 게 만 들 었 을 뿐 다시 작업 할 때연결 탱크 에서 휴면 상태의 연결 을 찾 아 깨 우 면 병발 능력 을 효과적으로 향상 시 키 고 연결 손실 을 줄 일 수 있다.연결 탱크 의 연결 수 는 모두 설정 할 수 있 습 니 다.
원본 코드 다운로드
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!

좋은 웹페이지 즐겨찾기