基于递归搜索以CSV格式创建新列

时间:2017-01-26 19:11:32

标签: powershell csv powershell-v4.0

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

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

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

我试图以此结束:

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

某些行不包含电子邮件地址或管理员的ID号,因此会引发另一个问题。我打算简单地添加&#34; Blank&#34;或&#34; X&#34;那些领域。

到目前为止,我已经敲定了这个简单的脚本来一次搜索一个。它有效,但它非常缓慢。从今天早上开始,花了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
            }
        }
    }
}

2 个答案:

答案 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

上面的代码是针对单个用户(以及他的经理)查找的,我不确定你的最终目标是什么,因为你说的很差(好吧,我不明白)。

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

答案 1 :(得分:1)

每次执行$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运算符会查找精确的字符串匹配(不区分大小写)。这可能是一个问题的一个原因是当你不打算使用正则表达式支持的复杂字符串匹配时使用-match可能导致与-eq相比性能降低,尽管可能不知不觉中如此。更重要的是,由于您有300,000多个用户,因此您的某些StaffNumber值必须至少为六位数,并且取决于您的CSV格式化方式(StaffNumber为零填充到最大数量数字?)并排序它可能匹配错误的用户。例如,如果$inputNumber12345,那么它将匹配用户12345,还会匹配用户112345123450等。如果您打算只允许按照确切的员工编号进行搜索,那么切换到上面的-eqHashTable解决方案就可以实现这一目标。