使用PowerShell比较两个CSV文件并更快地返回匹配值

时间:2018-03-29 15:42:48

标签: performance powershell csv

我使用此代码匹配两个CSV文件并获取我需要的列 在此代码中,我比较了数据Matricule nameFirstname,当我得到匹配时,我可以检索列' IGG'

但它很慢......(18行20分钟)

有人可以帮我这个吗?

这是我的代码:

foreach ($item in $fileContentIMM) 
{
    try
    {
        $Matricule = $item.'Matricule'
        $name = $item.'Nom'
        $firstname = $item.'Prenom'

        # find first matching row in $$fileContentMagic using wildcard
        $objMatch = $fileContentMagic | where { $_.'Matricule' -eq $Matricule -and $_.'NOM' -eq $name -and $_.'PRENOM' -eq $firstname}


        ##### check if any match found 
        if ($objMatch -eq $null)
        {
            $item  | ForEach-Object {
                $filechecktrue += [pscustomobject]@{
                    'MATRICULE' = $item.'Matricule'
                    'IGG' = 'noSet'
                    'NAME'  = $item.'Nom'
                    'FIRSTNAME' = $item.'Prenom'
                    'SERVICE' = $item.'Service'
                    'Immeuble'= $item.'Immeuble' 
                    'Niveau' = $item.'Niveau'
                    'Loc.' = $item.'Loc.'
                    'PDT' = $item.'PDT'
                    'Occ.' = $item.'Occ.'
                    'Site' = $item.'Site'
                }
            }
        }
        else
        {
            $item  | ForEach-Object {
                $filechecktrue += [pscustomobject]@{
                    'MATRICULE' = $item.'Matricule'
                    'IGG' = ($objMatch.'IGG' -join '/')
                    'NAME'  = $item.'Nom'
                    'FIRSTNAME' = $item.'Prenom'
                    'SERVICE' = $item.'Service'
                    'Immeuble'= $item.'Immeuble' 
                    'Niveau' = $item.'Niveau'
                    'Loc.' = $item.'Loc.'
                    'PDT' = $item.'PDT'
                    'Occ.' = $item.'Occ.'
                    'Site' = $item.'Site'
                }
            }

        }
    }
    catch
    {
        "ERROR: Problem reading line - skipping :" | Out-File $LogFile -Append -Force
        $item.nom + $item.prenom + $item.service| Out-File $LogFile -Append -Force
    }
}

3 个答案:

答案 0 :(得分:2)

我会读取您用于查找的文件,然后为此创建一个HashTable。 HashTables非常有效地进行查找。

尝试这样的事情,假设您在FileContentMagic中没有任何重复项:

# Use any character here which is guaranteed not to be present in the Matricule, Nom,
# or Prenom fields
$Delimiter = '|'

# Read the FileContent Magic into a HashTable for fast lookups
# The key is Matricule|Nom|Prenom
# The value is IGG joined with a forward slash
$FileContentMagic = @{}
Import-Csv -Path $FileContentMagicFileName | ForEach-Object {
    # Here we build our lookup key. The Trim() is just in case there's any leading or trailing
    # whitespace You can leave it out if you know you don't need it
    $Key = $_.Matricule.Trim(), $_.Nom.Trim(), $_.Prenom.Trim() -join $Delimiter

    # Since we only need the IGG value joined with a /, we'll just keep that
    $Value = $_.IGG -join '/'
    $FileContentMagic.Add($Key, $Value)
}

$FileContentIMM = Import-Csv -Path $FileContentIMMFileName

$FileCheckTrue = foreach ($item in $FileContentIMM) {
    $Key = $_.Matricule.Trim(), $_.Nom.Trim(), $_.Prenom.Trim() -join $Delimiter

    [PSCustomObject]@{
        'MATRICULE' = $item.'Matricule'
        'IGG'       = if ($FileContentMagic.ContainsKey($Key)) { $FileContentMagic[$Key] } else { 'noSet' }
        'NAME'      = $item.'Nom'
        'FIRSTNAME' = $item.'Prenom'
        'SERVICE'   = $item.'Service'
        'Immeuble'  = $item.'Immeuble' 
        'Niveau'    = $item.'Niveau'
        'Loc.'      = $item.'Loc.'
        'PDT'       = $item.'PDT'
        'Occ.'      = $item.'Occ.'
        'Site'      = $item.'Site'
    }
}

此外,只要您使用+=连接数组,就会带来显着的性能损失。避免使用它是值得的,因为每个赋值创建一个新数组,使用新项复制整个数组,然后丢弃旧数组。这是非常低效的。

如果$FileContentMagic包含重复的键,那么您应该更改HashTable的加载方式:

$FileContentMagic = @{}
Import-Csv -Path $FileContentMagicFileName | ForEach-Object {
    $Key = $_.Matricule.Trim(), $_.Nom.Trim(), $_.Prenom.Trim() -join $Delimiter
    if (!$FileContentMagic.ContainsKey($Key)) {
        $Value = $_.IGG -join '/'
        $FileContentMagic.Add($Key, $Value)
    }
    else {
        $FileContentMagic[$Key] += '/' + ($_.IGG -join '/')
    }
}

答案 1 :(得分:1)

我会简化这一点,但更改不应影响处理时间。我所做的唯一优化是将$ filechecktrue更改为更高内存效率的List。

不确定这实际上是否是脚本的慢速部分。这需要$fileContentMagic成为非常大的数组。

$filechecktrue = New-Object System.Collections.ArrayList

foreach ($item in $fileContentIMM) 
{
    try
    {
        $Matricule = $item.'Matricule'
        $name = $item.'Nom'
        $firstname = $item.'Prenom'

        # find first matching row in $fileContentMagic using wildcard
        $objMatch = $fileContentMagic | Where-Object { $_.'Matricule' -eq $Matricule -and $_.'NOM' -eq $name -and $_.'PRENOM' -eq $firstname}

        #Create results object with common properties
        $o += [pscustomobject]@{
            'MATRICULE' = $item.'Matricule'
            'IGG' = 'noSet'
            'NAME'  = $item.'Nom'
            'FIRSTNAME' = $item.'Prenom'
            'SERVICE' = $item.'Service'
            'Immeuble'= $item.'Immeuble' 
            'Niveau' = $item.'Niveau'
            'Loc.' = $item.'Loc.'
            'PDT' = $item.'PDT'
            'Occ.' = $item.'Occ.'
            'Site' = $item.'Site'
        }

        ##### check if any match found 
        if ($objMatch)
        {
            #if not null, set IGG value. No need for foreach as $item is already a "foreach-value".
            $o.IGG = ($objMatch.'IGG' -join '/')
        }

        #Add result to arraylist
        $filechecktrue.Add($o)
    }
    catch
    {
        "ERROR: Problem reading line - skipping :" | Out-File $LogFile -Append -Force
        $item.nom + $item.prenom + $item.service| Out-File $LogFile -Append -Force
    }
}

答案 2 :(得分:0)

你的第一个foreach在每次迭代时返回一个$ item-object,所以再次在代码块内的$ item上使用foreach(两次)是无稽之谈。

尝试此操作(删除冗余):

foreach ($item in $fileContentIMM) {
    try {
        # find first matching row in $fileContentMagic using wildcard
        $objMatch = $fileContentMagic | where { $_.'Matricule' eq $item.'Matricule'
                                           -and $_.'NOM' -eq $item.'Nom'
                                           -and $_.'PRENOM' -eq $item.'Prenom'}


        ##### check if any match found 
        if ($objMatch -eq $null) {
            $IGG = 'noSet'
        } else {
            $IGG = ($objMatch.'IGG' -join '/')
        }
        $filechecktrue += [pscustomobject]@{
            'MATRICULE' = $item.'Matricule'
            'IGG' = $IGG
            'NAME'  = $item.'Nom'
            'FIRSTNAME' = $item.'Prenom'
            'SERVICE' = $item.'Service'
            'Immeuble'= $item.'Immeuble' 
            'Niveau' = $item.'Niveau'
            'Loc.' = $item.'Loc.'
            'PDT' = $item.'PDT'
            'Occ.' = $item.'Occ.'
            'Site' = $item.'Site'

    } catch {
        "ERROR: Problem reading line - skipping :" | Out-File $LogFile -Append -Force
        $item.nom + $item.prenom + $item.service| Out-File $LogFile -Append -Force
    }
}