我在Windows 2012 R2上有一个PowerShell脚本,用于将数据库中的数据导出到CSV文件中。我在那里检查以避免双引号和文本限定必要的字段。我希望提高脚本的性能,因为它运行速度比我想要的慢一点(导出20GB / 2000万行),它只占用了大约10%的CPU。有没有人有任何改进建议?
$ConnectionString = "Data Source=server1; Database=Development; Trusted_Connection=True;";
$streamWriter = New-Object System.IO.StreamWriter ".\output.csv"
$sqlConn = New-Object System.Data.SqlClient.SqlConnection $ConnectionString
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.Connection = $sqlConn
$sqlCmd.CommandText = "SELECT * FROM Development.dbo.All_Opportunities WITH(NOLOCK)"
$sqlConn.Open();
$reader = $sqlCmd.ExecuteReader();
# Initialze the array the hold the values
$array = @()
for ( $i = 0 ; $i -lt $reader.FieldCount; $i++ )
{ $array += @($i) }
# Write Header
$streamWriter.Write($reader.GetName(0))
for ( $i = 1; $i -lt $reader.FieldCount; $i ++)
{ $streamWriter.Write($("," + $reader.GetName($i))) }
$streamWriter.WriteLine("") # Close the header line
while ($reader.Read())
{
# get the values;
$fieldCount = $reader.GetValues($array);
# add quotes if the values have a comma or double quote
for ($i = 0; $i -lt $array.Length; $i++)
{
if ($array[$i] -match "`"|,")
{
$array[$i] = '"' + $array[$i].Replace("`"", "`"`"").ToString() + '"';
}
}
$newRow = [string]::Join(",", $array);
$streamWriter.WriteLine($newRow)
}
$reader.Close();
$sqlConn.Close();
$streamWriter.Close();
答案 0 :(得分:0)
您可以将其拆分为作业并在后台启动它们 试试:
希望有所帮助
答案 1 :(得分:0)
所以,我在一年前遇到了类似的问题,尽管表格略小(约1 GB)。最初我刚刚使用过:
Import-Module -Name SqlServer -Cmdlet Read-SqlTableData;
Read-SqlTableData -ServerInstance $SqlServer -DatabaseName $Database -SchemaName $Schema -TableName $Table |
Export-Csv -Path $OutputFilePath -NoTypeInformation
虽然有效但是它使用了 ton 的内存(16 GB中的5 GB以上)并且运行了大约7到9分钟。所有这些测试都是在笔记本电脑中使用旋转金属盘,所以请记住以下内容。
我想知道我是否可以让它更快。我最初这样编写它,花了大约一半的时间,大约100 MB的RAM:
$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,因为我已经成功地与其他项目一起做了。我仍然有代码,但我发现它没有更快的工作,并占用了大约200 MB的RAM:
$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:30的时间内获得大约1 GB的数据。
我从未考虑并行性,因为您必须将查询分成4个相等的部分,以便您可以确定您获得完整的数据集,或者使用其他方式执行一些非常困难的流程管理Runspace池。即使这样,您也必须写入四个不同的文件,并最终将这些文件组合在一起。也许它会成功,但在那时我不再是一个有趣的问题。
最后,我刚刚使用“导入导出向导”创建了一个包,将其另存为包,然后使用DTExec.exe
运行它。对于1 GB的数据,大约需要45-60秒左右。唯一的缺点是你需要在构建包时指定表,它不会动态确定列,并且输出文件为UTF8是一种不合理的痛苦。
我确实发现bcp.exe
和sqlcmd.exe
更快。 BCP非常快,耗时20-30秒。但是,输出格式非常有限,特别是BCP难以使用。