【问题标题】:Creating new columns in CSV based on recursive search基于递归搜索在 CSV 中创建新列
【发布时间】:2017-06-12 09:01:50
【问题描述】:

我正在尝试处理一个只有三列的 15MB CSV 文件。例如:

StaffNumber,EmailAddress,Manager
123,ArthurDent@beeblebrox.com,456.

我需要搜索 CSV 文件的每一行,显示员工编号、电子邮件地址,然后获取经理的 ID 号并进行搜索,显示他们的员工编号和电子邮件,然后获取 他们的 em> 经理的 ID 和重复相同。最后,我需要让 CSV 文件的每一行都有用户 ID 和电子邮件,以及最多三个经理 ID 和电子邮件

我正在努力解决这个问题:

123,ArthurDent@beeblebrox.com,456,Marvin@beeblebrox.com,789,Zaphod@Beeblebrox.com,098,zaphod@beeblebrox.com

有些行不包含电子邮件地址或经理的 ID 号,所以这又是一个麻烦事。我打算在这些字段中简单地添加“空白”或“X”。

到目前为止,我已经敲定了这个简单的脚本来一次搜索一个。它可以工作,但是速度非常慢。从今天早上开始,通过该文件需要 6 个小时才能获得 0.31%。啊!

我读了很多遍,直到完全搞不懂如何最好地处理价值 15MB 的 CSV 数据、不同的 PowerShell 版本、本周最好的东西等等,而 PowerShell 与我常用的语言相去甚远(请遗憾我缺乏 PS技能。我只是想充实一个基本的测试脚本以获得一个想法)。

我目前正在运行 PowerShell v4,并且我知道与早期版本存在兼容性问题。我想尽量与未来的版本保持兼容。

处理此文件的最快方法是什么? 300,000 行只有 15MB,我不关心 RAM。我只是不知道如何更有效地运行此搜索。

$FilePath = "C:\Temp\DA-UserList.csv"
$DAUserlist = Import-CSV $FilePath

$inputNumber = Read-Host -Prompt "Employee ID Number"

$DAUser1 = $DAUserlist | Where{$inputNumber -match $_.StaffNumber}| Select -First 1
ForEach ($item in $DAUser1){
    $StaffNumber1 = $($item.StaffNumber)
    $EmailAddress1 = $($item.EmailAddress)
    $Manager1 = $($item.Manager)
    printf $item.StaffNumber
    printf ","
    printf $EmailAddress1
    $DAUser2 = $DAUserlist | Where{$Manager1 -match $_.StaffNumber}| Select -First 1
    ForEach ($item in $DAUser2){
        $StaffNumber2 = $($item.StaffNumber)
        $EmailAddress2 = $($item.EmailAddress)
        $Manager2 = $($item.Manager)
        printf ","
        printf $StaffNumber2
        printf ","
        printf $EmailAddress2
        $DAUser3 = $DAUserlist | Where{$Manager2 -match $_.StaffNumber}| Select -First 1
        ForEach ($item in $DAUser3){
            $StaffNumber3 = $($item.StaffNumber)
            $EmailAddress3 = $($item.EmailAddress)
            $Manager3 = $($item.Manager)
            printf ","
            printf $StaffNumber3
            printf ","
            printf $EmailAddress3
            $DAUser4 = $DAUserlist | Where{$Manager3 -match $_.StaffNumber}| Select -First 1
            ForEach ($item in $DAUser4){
                $StaffNumber4 = $($item.StaffNumber)
                $EmailAddress4 = $($item.EmailAddress)
                $Manager4 = $($item.Manager)
                printf ","
                printf $StaffNumber4
                printf ","
                printf $EmailAddress4
                printf \n
            }
        }
    }
}

【问题讨论】:

    标签: powershell csv powershell-4.0


    【解决方案1】:

    好吧,我认为对于 15MB 的文件,您不需要任何核心优化(至少我是这么认为的)。因此,您要使用的是递归函数,因为您一遍又一遍地做同样的事情。

    $data = Import-Csv "C:\Temp\DA-UserList.csv"
    $i = 0
    
    function Get-CsvUser {
        param(
            [string]$id
        )
    
        $data.Where({$_.StaffNumber -eq $id}, 'First', 1)
    }
    
    function Get-CsvNested {
        param(
            [string]$id
        )
    
        $user = Get-CsvUser $id
        Get-CsvUser -id $user.Manager | % { 
            while ($global:i -lt 3) { # using global here to avoid circular execution
                $global:i++
    
                Write-Output "User: $($user.EmailAddress)"
                Write-Output "His Manager: $($_.EmailAddress)"
    
                "" # to output an empty string
                Get-CsvNested -id $_.StaffNumber
            }
        }
    }
    

    这样至少看起来会更好,更容易理解,至于性能,尝试使用.where()method。它更快。
    此外,您可以将 CSV 拆分为块并创建一个单独的进程来解析块(想想 Start-Job 或更好的Start-RSJob

    上面的代码是供单个用户(和他的经理)查找的,我不确定你的最终目标是什么,因为你的措辞很糟糕(好吧,我不明白)。

    如果您需要进一步的帮助,请在此处(或某处,查看我的个人资料)联系我,我们可以解决问题。

    【讨论】:

      【解决方案2】:

      每次执行$DAUserlist | Where { # ... } | Select -First 1 之类的操作时,您都在对 300,000 条记录进行线性搜索。您可以像这样使用Group-Object cmdlet 来加快速度...

      $DAUserlist = Import-CSV $FilePath
      $DAUsersByStaffNumber = $DAUserlist | Group-Object -Property 'StaffNumber' -AsHashTable
      

      $DAUsersByStaffNumber 包含一个HashTable 实例,将每个员工编号映射到一个用户记录。然后可以用以下代码替换您查找给定用户及其三个经理的代码...

      $DAUser1 = $DAUsersByStaffNumber[$inputNumber]
      # ...
      $DAUser2 = $DAUsersByStaffNumber[$Manager1]
      # ...
      $DAUser3 = $DAUsersByStaffNumber[$Manager2]
      # ...
      $DAUser4 = $DAUsersByStaffNumber[$Manager3]
      

      这会稍微简化代码并使您的查找更有效率。

      另外,需要注意的是,当您按员工编号过滤用户列表时,您使用的是支持正则表达式的 -match 运算符,而 -eq 运算符将寻找精确的字符串匹配(不区分大小写)。这可能是一个问题的一个原因是,与-eq 相比,当您不打算进行正则表达式支持的复杂字符串匹配时使用-match 可能会导致性能下降,尽管可能难以察觉。更重要的是,既然您有 300,000 多个用户,那么您的一些 StaffNumber 值必须至少为六位数长,并且取决于您的 CSV 格式(StaffNumber 是否用零填充到最大位数?)和排序后,您可能会匹配错误的用户。例如,如果$inputNumber12345,那么它将匹配用户12345,但也匹配用户112345123450 等。如果您打算只允许按确切的员工编号进行搜索,那么切换到-eq 或上面的HashTable 解决方案将实现这一目标。

      【讨论】:

        猜你喜欢
        • 2020-10-01
        • 2018-04-03
        • 1970-01-01
        • 2016-04-02
        • 1970-01-01
        • 1970-01-01
        • 2015-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多