PowerShell实现CSV数据与接口返回值高效匹配的方法
问题场景
- 共有4个CSV文件,单文件约15000行数据,数据格式示例:
number,"surname","forename","emailAddress","taxIdentifier" 100238963,"Smith","John","john.smith@gmail.com","xxxxxxxxxxxx"
- 业务逻辑:按9999行/批次读取数据,生成校验用JSON提交接口,接口返回校验通过的用户
number字段值,需要从原始用户数据中匹配对应编号的完整人员信息导出。 - 当前问题:采用嵌套循环逐行比对的方式实现匹配,执行耗时过长,需要更高效的实现方案。
原有实现代码
CSV读取与请求体生成代码
$Files = Get-ChildItem -Path "$Folders\\*" -Include *.csv -Force foreach ($File in $Files){ $fname = $file $fname = (Split-Path $File.name -leaf).ToString().Replace(".csv", "") $Savefile = $fname+ "_Cleaned.csv" $users = Import-Csv $File $body = "{`"requestId`": `"144x25`",`"items`": [" $batchSize = 9999 $batchNum = 0 $row = 0 while ($row -lt $users.Count) { $test = $users[$row..($row + $batchSize - 1)] foreach ($user in $test) { $nr = $user.number $tax = $user.taxIdentifier $body += "{`"itemId`": `"$nr`",`"subjectId`": `"$tax`"}," }
响应匹配与结果导出代码
$Result = @() foreach ($1 in $response.allowedItemIds) { foreach ($2 in $Users){ If ($2.number -like $1) { $Result += [pscustomobject]@{ number = $2.number Surname = $2.surname Forename = $2.forename Email = $2.emailaddress Taxidendifier = $2.taxIdentifier } } } } $Result | Export-Csv -path "$folders\$savefile" -NoTypeInformation -Append $row += $batchSize $batchNum++
核心性能瓶颈
- 嵌套循环匹配时间复杂度为O(n*m):每匹配一个返回的ID就要遍历全量用户列表,数据量上涨时耗时呈线性倍增。
- 字符串反复拼接开销大:手动用
+=拼接JSON请求体时,每次操作都会重新分配内存、复制整个已有字符串,大批次下内存和时间成本极高。 - 普通数组追加效率低:用
+=向PowerShell固定大小数组追加元素时,每次都会新建数组复制全部已有内容,结果集越大损耗越明显。 - 匹配算子冗余:ID是精确匹配场景,使用支持通配符的
-like判断比直接等值比较多了不必要的解析开销。
优化方案
- 用哈希表构建用户索引:提前把用户数据按
number字段存为键值对,匹配时直接按键查找,单条匹配时间复杂度降到O(1),总匹配复杂度从O(n*m)降到O(n+m)。 - 用泛型列表收集结果:改用
System.Collections.Generic.List类型存储结果,追加元素不需要复制整个数组,性能提升明显。 - 用原生序列化生成JSON:不要手动拼接字符串,直接构造请求对象后用
ConvertTo-Json序列化,既避免语法错误,性能也远高于手动拼接。 - 精确匹配场景用
-eq替代-like,减少不必要的通配符匹配开销。
优化后完整代码
$Files = Get-ChildItem -Path "$Folders\*" -Include *.csv -Force $batchSize = 9999 $requestId = "144x25" foreach ($File in $Files){ $fname = (Split-Path $File.Name -Leaf).Replace(".csv", "") $Savefile = Join-Path $Folders ($fname + "_Cleaned.csv") # 提前清理旧结果文件,避免重复追加 if (Test-Path $Savefile) { Remove-Item $Savefile -Force } # 导入CSV并构建以number为键的哈希索引 $users = Import-Csv $File $userIndex = @{} foreach ($user in $users) { $userIndex[$user.number] = $user } $row = 0 while ($row -lt $users.Count) { # 处理批次边界,避免数组越界 $endRow = [Math]::Min($row + $batchSize - 1, $users.Count - 1) $currentBatch = $users[$row..$endRow] # 构造请求对象直接序列化生成JSON,无需手动拼接 $requestBody = [PSCustomObject]@{ requestId = $requestId items = $currentBatch | ForEach-Object { [PSCustomObject]@{ itemId = $_.number subjectId = $_.taxIdentifier } } } | ConvertTo-Json -Depth 10 -Compress <# 此处补充调用接口提交$requestBody、获取$response的业务逻辑 #> # 用泛型List收集匹配结果,追加效率远高于普通数组 $result = [System.Collections.Generic.List[PSObject]]::new() foreach ($allowedId in $response.allowedItemIds) { # 直接从哈希索引取匹配用户,无需遍历全量列表 if ($userIndex.ContainsKey($allowedId)) { $matchedUser = $userIndex[$allowedId] $result.Add([PSCustomObject]@{ number = $matchedUser.number Surname = $matchedUser.surname Forename = $matchedUser.forename Email = $matchedUser.emailaddress Taxidendifier = $matchedUser.taxIdentifier }) } } # 导出当前批次匹配结果 $result | Export-Csv -Path $Savefile -NoTypeInformation -Append $row += $batchSize } }
15000行量级数据下,哈希索引匹配的耗时比原有嵌套循环降低99%以上,同时可以避免手动拼接JSON带来的格式错误问题。
内容的提问来源于stack exchange,提问作者user17461080
相关产品推荐
相关产品推荐

