快速比较 powershell 中的两个大 csv(行和列)

Fast compare two large csv(boths rows and columns) in powershell

我有两个大型 CSV 文件要比较。 Bosth csvs 基本上是相隔 1 天来自同一系统的数据。行数约为 12k,列数约为 30。

目的是确定主键(#ID)的哪些列数据发生了变化。

我的想法是遍历 CSV 以识别哪些行已更改并将它们转储到单独的 csvs 中。完成后,我再次遍历更改行,并确定列中的确切更改。

    NewCSV = Import-Csv -Path ".\Data_A.csv"
    OldCSV = Import-Csv -Path ".\Data_B.csv"
     
   foreach ($LineNew in $NewCSV)
    {
        ForEach ($LineOld in $OldCSV)
        {
            If($LineNew -eq $LineOld)
            {
                Write-Host $LineNew, " Match"
            }else{
                Write-Host $LineNew, " Not Match"
            }
        }
    }

但是一旦 运行 循环,就需要永远 运行 12k 行。我希望必须有一种更有效的方法来比较大文件 powershell。更快的东西。

好吧,你可以试一试,我并不是说 已经指出的内容会很快,但它应该给你一个很好的并排视角来比较已经指出的内容从 OldCsv 更改为 NewCsv。

注意:那些在两个 CSV 上具有相同值的单元格将被忽略。

$NewCSV = Import-Csv -Path ".\Data_A.csv"
$OldCSV = Import-Csv -Path ".\Data_B.csv" | Group-Object ID -AsHashTable -AsString

$properties = $newCsv[0].PSObject.Properties.Name

$result = foreach($line in $NewCSV)
{
    if($ref = $OldCSV[$line.ID])
    {
        foreach($prop in $properties)
        {
            if($line.$prop -ne $ref.$prop)
            {
                [pscustomobject]@{
                    ID = $line.ID
                    Property = $prop
                    OldValue = $ref.$prop
                    NewValue = $line.$prop
                }
            }
        }
        continue
    }

    Write-Warning "ID $($line.ID) could not be found on Old Csv!!"
}

作为 ,您编写了一个具有 二次 时间复杂度(O(n²) Big-O 表示法)的算法 - 每次输入大小翻倍,执行的计算量增加 4 倍。

为避免这种情况,我建议使用哈希表或其他字典类型来保存每个数据集,并使用输入中的主键作为字典键。通过这种方式,您可以获得相应记录的恒定时间查找,并且您的算法的时间复杂度变得接近线性(O(2n + k)):

$NewCSV = @{}
Import-Csv -Path ".\Data_A.csv" |ForEach-Object {
  $NewCSV[$_.ID] = $_
}

$OldCSV = @{}
Import-Csv -Path ".\Data_B.csv" |ForEach-Object {
  $OldCSV[$_.ID] = $_
}

现在我们可以通过 ID 有效地解析每一行,我们可以检查整个数据集,每个数据集都有一个独立的循环:

foreach($entry in $NewCSV.GetEnumerator()){
  if(-not $OldCSV.ContainsKey($entry.Key)){
    # $entry.Value is a new row, not seen in the old data set
  }

  $newRow = $entry.Value
  $oldRow = $OldCSV[$entry.Key]

  # do the individual comparison of the rows here
}

像上面一样做另一个循环,但是用 $NewCSV 代替 $OldCSV 到 find/detect 删除。