【问题标题】:Accelerate table adjustment加速表调整
【发布时间】:2017-12-01 10:07:57
【问题描述】:

在以下情况下,我需要将三个 CSV 格式的文本文件合并为一个:

pricelist-1.txt 中有一个 ProductID,它指向表 cnet-product-de.txt - 也指向 ProductID。 在 cnet-product-de.txt 上,MarketingTextID 指向表 cnet-text-de.txt - 指向 ID

现在我想将这三个文件与一个脚本合并为一个 CSV 文件。 最后是ProductID;描述;制造商零件编号;净价; NetRetailPrice + 文本应从 cnet-text-en.txt 上传。

脚本有效,但由于双循环和巨大的 txt 文件(最多 300'000 行),它需要永远(超过 8 小时)。 有谁知道如何加快我的脚本?如果您不了解条件,请不要犹豫,因为我不是母语人士。

#start timer
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

#Declaration
$temp = @()
$merged = @()

#clear existent txt
Clear-Content -Path "C:\temp\ALSO\merged.txt"

#read CSVs
$csvprice = Import-csv -path "C:\temp\ALSO\pricelist-1.txt" -Delimiter ';'
$csvtext = Import-Csv -path "C:\temp\ALSO\cnet-text-de.txt" -Delimiter "`t"  

#Read CSV / Group by MarketingTextID / delete multiple ProductID entries
$PAMID = Import-Csv -path "C:\temp\ALSO\cnet-product-de\cnet-product-de.txt" -Delimiter "`t" |
    Select-Object ProductID, MarketingTextID | 
    Group-Object ProductID | 
    ForEach-Object {
    [PsCustomObject]@{
        ProductID       = $_.group.ProductID | Get-Unique
        MarketingTextID = $_.Group.MarketingTextID -join ','
    }
}

#get a single row from $PAMID
ForEach ($ID1 in $PAMID) {

    #Split the MarketingTextIDs
    $1 = $ID1.MarketingTextID.Split(",")[0]
    $2 = $ID1.MarketingTextID.Split(",")[1]
    $3 = $ID1.MarketingTextID.Split(",")[2]
    $4 = $ID1.MarketingTextID.Split(",")[3]

    #get a single row from $csvtext
    foreach ($ID in $csvtext) {

        #Comparison with the individual MarketingTextIDs and add to $temp variable
        if (($ID.ID -eq $1) -Or ($ID.ID -eq $2) -Or ($ID.ID -eq $3) -Or ($ID.ID -eq $3)) {
            $temp += $ID1 | Select-Object *, @{name = "Text"; expression = {$ID.Text}}
            break
        }
        else {
            continue
        }

    }
}

#Get a single row from $temp
foreach ($tempid in $temp) {

    #Declaration
    $tid = $tempid.ProductID
    $tmid = $tempid.MarketingTextID
    $ttext = $tempid.Text

    #Get a single row from $csvprice
    foreach ($Price in $csvprice) {

        #Comparison ProductIDs and add to $merged Variable
        if ($Price.ProductID -eq $tid) {
            $Price = $Price | select *, @{name = "MarketingTextID"; expression = {$tmid}}
            $Price = $Price | select *, @{name = "Text"; expression = {$ttext}}
            $merged += $Price
            break
        }
        else {
            continue
        }
    }

}
#Export to txt in UTF8 format
$merged | Export-Csv -Path "C:\temp\Also\merged.txt" -Encoding UTF8

#Exit and output timer
$stopwatch.stop()
Write-Host "The script took $($stopwatch.elapsed.totalminutes) minutes"

【问题讨论】:

  • 你应该看看Join-Object cmdlet 和-contains 运算符,我认为...
  • 我同意 Join-Object 可能是要走的路。您尝试做的类似于关系连接,这是 SQL 非常擅长的事情。大多数数据库系统都包含一个优化器,它会找出一个相当有效的策略,一个可能涉及在主内存中创建索引信息的策略。您的算法看起来像是蛮力。当你有这么多数据时,你就不能那样做。

标签: performance powershell foreach import-from-csv


【解决方案1】:

如果您需要交叉连接 300000 行 CSV,那么您正在查看内循环的 300 亿次 次迭代。即使每个都花费不到一毫秒,您也会看到几个小时:就像您一样。

您需要减少迭代次数。目前您正在循环结束时执行continue ......并且您只寻找第一个结果。所以内部循环只得到一个值,因此:

foreach ($ID in $csvtext){
  if(($ID.ID -eq $1) -Or ($ID.ID -eq $2) -Or ($ID.ID -eq $3) -Or ($ID.ID -eq $3)) {
    $temp += $ID1 | select *,@{name="Text";expression={$ID.Text}}
    break
  } else { 
     continue
  }
}

可以变成:

$opts = $1, $2, $3, $4;
$innerRes = $csvtext | Where-Object $_.ID -in $opts |
             select-object -first 1 |
             select-object *,@{name="Text";expression={$_.Text}}

外循环上的类似转换意味着您可以将管道分配给结果而不是附加到数组(前者比后者快)。

通常:使用平台的核心范例(在 PowerShell 中:管道)通常比使用其他线路的核心范例效果更好。

最后:这可能是您的工具错误的情况:将每个 CSV 上传到数据库并使用 SQL(专为此类操作而设计),但您需要在 SQL 中使用集合操作,而不是强制性的代码。

【讨论】:

  • 一般[...]管道我真的很喜欢这个。我认为它可以作为面向对象行为的补充,这将使大量使用拆分操作过时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 2016-05-08
  • 1970-01-01
  • 2021-06-11
  • 2016-08-18
相关资源
最近更新 更多