C\#데 이 터 를 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()를 사용 하여 연결 을 풀 때 데이터 베 이 스 를 진정 으로 닫 지 않 았 습 니 다.연결 을 휴면 과 유사 하 게 존재 하 게 만 들 었 을 뿐 다시 작업 할 때연결 탱크 에서 휴면 상태의 연결 을 찾 아 깨 우 면 병발 능력 을 효과적으로 향상 시 키 고 연결 손실 을 줄 일 수 있다.연결 탱크 의 연결 수 는 모두 설정 할 수 있 습 니 다.
원본 코드 다운로드
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebView2를 Visual Studio 2017 Express에서 사용할 수 있을 때까지Evergreen .Net Framework SDK 4.8 VisualStudio2017에서 NuGet을 사용하기 때문에 패키지 관리 방법을 packages.config 대신 PackageReference를 사용해야...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.