도움을 희망, 약간 작은 테이블 (~ 1GB)이긴하지만. 그것은 일
Import-Module -Name SqlServer -Cmdlet Read-SqlTableData;
Read-SqlTableData -ServerInstance $SqlServer -DatabaseName $Database -SchemaName $Schema -TableName $Table |
Export-Csv -Path $OutputFilePath -NoTypeInformation
하지만 (GB 16에서 5 + GB)를 t 메모리의을 사용하고 실행하는 데 7-9 분에 나섭니다 : 처음에 그냥 사용했다. 이 모든 테스트는 노트북에 회전하는 금속 디스크를 사용하여 수행되었으므로 다음 사항도 염두에 두십시오.
내가 더 빨리 진행할 수 있을지 궁금해했습니다. 나는 처음에 절반 정도 시간이 걸렸하는이처럼 쓰고, RAM 약 100메가바이트 :
$SqlServer = '...';
$SqlDatabase = '...';
$OutputFilePath = '...';
$SqlQuery = '...';
$SqlConnectionString = 'Data Source={0};Initial Catalog={1};Integrated Security=SSPI' -f $SqlServer, $SqlDatabase;
$Utf8NoBOM = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false;
$StreamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList $OutputFilePath, $Utf8NoBOM;
$CsvDelimiter = '"';
$CsvDelimiterEscape = '""';
$CsvSeparator = ',';
$SQLConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $SqlConnectionString;
$SqlCommand = $SQLConnection.CreateCommand();
$SqlCommand.CommandText = $SqlQuery;
$SQLConnection.Open();
$SqlDataReader = $SqlCommand.ExecuteReader();
for ($Field = 0; $Field -lt $SqlDataReader.FieldCount; $Field++) {
if ($Field -gt 0) { $StreamWriter.Write($CsvSeparator); }
$StreamWriter.Write($CsvDelimiter);
$StreamWriter.Write($SqlDataReader.GetName($Field).Replace($CsvDelimiter, $CsvDelimiterEscape));
$StreamWriter.Write($CsvDelimiter);
}
$StreamWriter.WriteLine();
while ($SqlDataReader.Read()) {
for ($Field = 0; $Field -lt $SqlDataReader.FieldCount; $Field++) {
if ($Field -gt 0) { $StreamWriter.Write($CsvSeparator); }
$StreamWriter.Write($CsvDelimiter);
$StreamWriter.Write($SqlDataReader.GetValue($Field).ToString().Replace($CsvDelimiter, $CsvDelimiterEscape));
$StreamWriter.Write($CsvDelimiter);
}
$StreamWriter.WriteLine();
}
$SqlDataReader.Close();
$SqlDataReader.Dispose();
$SQLConnection.Close();
$SQLConnection.Dispose();
$StreamWriter.Close();
$StreamWriter.Dispose();
당신이 볼 수 있듯이, 그것은 기본적으로 당신과 같은 패턴입니다.
나는 그것을 더 향상시킬 수 있을지 궁금해했기 때문에 다른 프로젝트에서 성공했기 때문에 StringBuilder를 추가하려고 시도했습니다. 난 여전히 코드를 가지고,하지만 난 그게 더 빨리 작동하지 않는 것을 발견하고, RAM 200MB의 가지고 가지 않았다 : 아무리 내가, 내가 아래를 얻을 수가 없습니다 시도 무엇
$SqlServer = '...'
$SqlDatabase = '...'
$OutputFilePath = '...'
$SqlQuery = '...';
$SqlConnectionString = 'Data Source={0};Initial Catalog={1};Integrated Security=SSPI' -f $SqlServer, $SqlDatabase;
$StringBuilderBufferSize = 50MB;
$StringBuilder = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($StringBuilderBufferSize + 1MB);
$Utf8NoBOM = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false;
$StreamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList $OutputFilePath, $Utf8NoBOM;
$CsvDelimiter = '"';
$CsvDelimiterEscape = '""';
$CsvSeparator = ',';
$SQLConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $SqlConnectionString;
$SqlCommand = $SQLConnection.CreateCommand();
$SqlCommand.CommandText = $SqlQuery;
$SQLConnection.Open();
$SqlDataReader = $SqlCommand.ExecuteReader();
for ($Field = 0; $Field -lt $SqlDataReader.FieldCount; $Field++) {
if ($Field -gt 0) { [void]$StringBuilder.Append($CsvSeparator); }
[void]$StringBuilder.Append($CsvDelimiter);
[void]$StringBuilder.Append($SqlDataReader.GetName($Field).Replace($CsvDelimiter, $CsvDelimiterEscape));
[void]$StringBuilder.Append($CsvDelimiter);
}
[void]$StringBuilder.AppendLine();
while ($SqlDataReader.Read()) {
for ($Field = 0; $Field -lt $SqlDataReader.FieldCount; $Field++) {
if ($Field -gt 0) { [void]$StringBuilder.Append($CsvSeparator); }
[void]$StringBuilder.Append($CsvDelimiter);
[void]$StringBuilder.Append($SqlDataReader.GetValue($Field).ToString().Replace($CsvDelimiter, $CsvDelimiterEscape));
[void]$StringBuilder.Append($CsvDelimiter);
}
[void]$StringBuilder.AppendLine();
if ($StringBuilder.Length -ge $StringBuilderBufferSize) {
$StreamWriter.Write($StringBuilder.ToString());
[void]$StringBuilder.Clear();
}
}
$SqlDataReader.Close();
$SqlDataReader.Dispose();
$SQLConnection.Close();
$SQLConnection.Dispose();
$StreamWriter.Write($StringBuilder.ToString());
$StreamWriter.Close();
$StreamWriter.Dispose();
을 ~ 4 : 약 1GB의 데이터는 30입니다.
쿼리를 4 개의 동등한 부분으로 나눠서 전체 데이터 세트를 얻을 수 있는지 또는 Runspace 풀을 사용하여 매우 어려운 프로세스 관리를 수행해야하는지 알 수 있으므로 결코 병렬 처리를 고려하지 않았습니다. 그렇다면 네 개의 서로 다른 파일에 쓰고 결국 다시 함께 결합해야합니다. 어쩌면 효과가 있을지 모르지만 그 시점에서 더 이상 나에게 흥미로운 문제는 아니 었습니다.
결국 가져 오기 내보내기 마법사를 사용하여 패키지를 만들고 패키지로 저장 한 후 DTExec.exe
을 사용하여 실행했습니다. 1GB의 데이터에는 약 45-60 초 정도 걸립니다. 단점은 패키지를 빌드 할 때 테이블을 지정해야하며 동적으로 열을 결정하지 않으며 출력 파일을 UTF8로 가져 오는 것이 무리라는 점입니다.
나는 bcp.exe
과 sqlcmd.exe
가 더 빠르다는 것을 발견했다. BCP는 매우 빠르며 20-30 초가 걸렸습니다. 그러나 출력 형식은 극히 제한되어 있으며 특히 BCP는 불필요하게 사용하기가 어렵습니다.
RAM을 활용합니다. 그것이 바로 그 때문입니다. 각각의 레코드를 개별적으로'$ StreamWriter'에 쓰고 프로세스를 디스크 IO에 바인딩하는 대신'StringBuilder'를 버퍼로 써주세요. 그런 다음'StringBuilder'가 주어진 길이 인 50-100 MB에 도달하면'StringBuilder'를 문자열로 변환하고'$ StreamWriter'에 쓰고'StringBuilder'를 지운 다음 계속하십시오.마지막에'StringBuilder'를 마지막으로 플러시하십시오. –
멋진 솔루션처럼 들립니다. 예가 대단 할 것입니다. –
사실, 나는 그것을 되 찾습니다. 그것에 대해 더 생각하면, 나는 다른 상황을 생각하고 있습니다. 'StreamWriter' 자체는 이미 버퍼를 가지고 있기 때문에 두번째 성능을 향상시키지 않을 것입니다. –