You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速PowerShell中从DataSet向SQL批量插入数据?

问题:PowerShell逐行插入DataSet数据到SQL表耗时过长,如何加速或并行插入?

运行以下PowerShell脚本从DataSet向SQL表逐行插入数据时,耗时极长,插入时间随数据行数增加而增长。请问是否有方法加速该过程,或拆分数据实现并行插入?

for ( $i = 0; $i -lt $DataSet.Tables[0].Rows.Count; $i++) { 
  try { 
    $valuestr = New-Object -TypeName System.Text.StringBuilder 
    for ( $x = 0; $x -lt 11; $x++) { 
      if ($x -lt 10) { 
        [void]$valuestr.Append("'" + $DataSet.Tables[0].Rows[$i][$x].ToString().Trim().Replace("'", "/") + "',") 
      } 
      else { 
        [void]$valuestr.Append("'" + $DataSet.Tables[0].Rows[$i][$x].ToString().Trim().Replace("'", "/") + "'") 
      } 
    }
    [string]$inputstr = $valuestr.ToString()
    [char[]]$values = $inputstr.ToCharArray()
    [string]$output = ''
    foreach ($letter in $values) { 
            
      [int]$value = [Convert]::ToInt32($letter)
      [string]$hexOutput = [String]::Format("{0:X}", $value)
      switch ($hexOutput) {
        "627" { $output += "ا"; Break }
        default { $output += [Convert]::Tostring($letter); break }
      }
    }
    $sqlCmdd.CommandText = "INSERT INTO $sqlTable ([ICN],[ICN_NODE],[PARTITION_ID],[ICN_CREATE_DT] ,[ICN_SEQ_NUM],[ICN_SUBNUM],[ICN_TAG_ID],[ICN_TAG_VER],[BLK_NUM],[BLK_LEN] ,[FREE_FORM_TXT]) Values (" + $output.ToString() + ")" 
    $sqlCmdd.ExecuteNonQuery() 
  }
  catch { $_.Exception.Message | Out-File C:\log\log.txt -Append } 
}
解决方案

1. 使用SqlBulkCopy(最推荐的高效方案)

这是SQL Server官方提供的批量写入工具,性能远高于逐行插入,能将数据写入效率提升一个数量级以上。核心思路是直接将预处理后的DataTable批量写入SQL表:

# 先预处理DataTable中的数据(替换特殊字符、转换目标字符)
foreach ($row in $DataSet.Tables[0].Rows) {
    for ($x = 0; $x -lt 11; $x++) {
        $rawValue = $row[$x].ToString().Trim()
        # 替换单引号避免SQL语法错误
        $processedValue = $rawValue.Replace("'", "/")
        # 处理字符转换逻辑
        $charArray = $processedValue.ToCharArray()
        $finalValue = ''
        foreach ($letter in $charArray) {
            $hex = [String]::Format("{0:X}", [Convert]::ToInt32($letter))
            $finalValue += if ($hex -eq "627") { "ا" } else { $letter.ToString() }
        }
        $row[$x] = $finalValue
    }
}

# 初始化SqlBulkCopy并执行批量插入
$connectionString = "你的数据库连接字符串"
$bulkCopy = New-Object Data.SqlClient.SqlBulkCopy($connectionString)
$bulkCopy.DestinationTableName = $sqlTable

# 自动映射列(如果DataSet列名与SQL表列名完全一致可省略)
foreach ($col in $DataSet.Tables[0].Columns) {
    $bulkCopy.ColumnMappings.Add($col.ColumnName, $col.ColumnName) | Out-Null
}

try {
    $bulkCopy.WriteToServer($DataSet.Tables[0])
} catch {
    $_.Exception.Message | Out-File C:\log\log.txt -Append
} finally {
    $bulkCopy.Close()
}

2. 参数化批量INSERT(兼容场景备选)

如果无法使用SqlBulkCopy,可将多行数据合并为一个INSERT语句(比如一次插入100-1000行),配合参数化查询减少网络往返次数,同时避免SQL注入风险:

$batchSize = 100
$rowsCount = $DataSet.Tables[0].Rows.Count
$connection = New-Object Data.SqlClient.SqlConnection($connectionString)
$connection.Open()
$transaction = $connection.BeginTransaction()

try {
    for ($batchStart = 0; $batchStart -lt $rowsCount; $batchStart += $batchSize) {
        $batchEnd = [math]::Min($batchStart + $batchSize - 1, $rowsCount - 1)
        $valueClauses = @()
        $params = @()

        for ($i = $batchStart; $i -le $batchEnd; $i++) {
            $row = $DataSet.Tables[0].Rows[$i]
            $paramNames = @()
            for ($x = 0; $x -lt 11; $x++) {
                $paramName = "@p_$($i)_$($x)"
                $paramNames += $paramName
                # 预处理参数值
                $rawValue = $row[$x].ToString().Trim().Replace("'", "/")
                $charArray = $rawValue.ToCharArray()
                $finalValue = ''
                foreach ($letter in $charArray) {
                    $hex = [String]::Format("{0:X}", [Convert]::ToInt32($letter))
                    $finalValue += if ($hex -eq "627") { "ا" } else { $letter.ToString() }
                }
                $params += New-Object Data.SqlClient.SqlParameter($paramName, $finalValue)
            }
            $valueClauses += "($($paramNames -join ','))"
        }

        $sql = "INSERT INTO $sqlTable ([ICN],[ICN_NODE],[PARTITION_ID],[ICN_CREATE_DT],[ICN_SEQ_NUM],[ICN_SUBNUM],[ICN_TAG_ID],[ICN_TAG_VER],[BLK_NUM],[BLK_LEN],[FREE_FORM_TXT]) VALUES $($valueClauses -join ',')"
        $cmd = New-Object Data.SqlClient.SqlCommand($sql, $connection, $transaction)
        $cmd.Parameters.AddRange($params)
        $cmd.ExecuteNonQuery()
    }
    $transaction.Commit()
} catch {
    $transaction.Rollback()
    $_.Exception.Message | Out-File C:\log\log.txt -Append
} finally {
    $connection.Close()
}

3. 并行插入(大数据集场景补充)

针对超大规模数据集,可拆分数据为多个批次,通过RunspacePool实现并行插入,注意控制并行度避免数据库锁竞争:

$batchSize = 2000
$rowsCount = $DataSet.Tables[0].Rows.Count
$batches = [math]::Ceiling($rowsCount / $batchSize)
$connectionString = "你的数据库连接字符串"

# 创建Runspace池,控制并行度(建议4-8,根据服务器性能调整)
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 6)
$runspacePool.Open()
$jobs = @()

for ($batchIdx = 0; $batchIdx -lt $batches; $batchIdx++) {
    $startRow = $batchIdx * $batchSize
    $endRow = [math]::Min(($batchIdx + 1) * $batchSize - 1, $rowsCount - 1)

    $scriptBlock = {
        param($start, $end, $sourceTable, $sqlTable, $connStr)
        # 创建子DataTable
        $subTable = $sourceTable.Clone()
        for ($i = $start; $i -le $end; $i++) {
            $subTable.ImportRow($sourceTable.Rows[$i])
        }
        # 预处理子表数据
        foreach ($row in $subTable.Rows) {
            for ($x = 0; $x -lt 11; $x++) {
                $rawValue = $row[$x].ToString().Trim().Replace("'", "/")
                $charArray = $rawValue.ToCharArray()
                $finalValue = ''
                foreach ($letter in $charArray) {
                    $hex = [String]::Format("{0:X}", [Convert]::ToInt32($letter))
                    $finalValue += if ($hex -eq "627") { "ا" } else { $letter.ToString() }
                }
                $row[$x] = $finalValue
            }
        }
        # 批量插入子表
        $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy($connStr)
        $bulkCopy.DestinationTableName = $sqlTable
        $bulkCopy.WriteToServer($subTable)
        $bulkCopy.Close()
    }

    $job = [PowerShell]::Create().AddScript($scriptBlock).AddArgument($startRow).AddArgument($endRow).AddArgument($DataSet.Tables[0]).AddArgument($sqlTable).AddArgument($connectionString)
    $job.RunspacePool = $runspacePool
    $jobs += $job
    $null = $job.BeginInvoke()
}

# 等待所有并行任务完成
foreach ($job in $jobs) {
    $job.EndInvoke($job.BeginInvoke())
    $job.Dispose()
}
$runspacePool.Close()
$runspacePool.Dispose()

4. 原有代码的局部优化

如果必须保留逐行插入逻辑,可做以下优化减少耗时:

  • 复用StringBuilder对象,避免循环内重复创建
  • 直接处理字段值而非拼接后的字符串,减少字符遍历次数
  • 开启数据库事务,减少日志写入开销

内容的提问来源于stack exchange,提问作者MustaMusta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:40:42