Производительность
Прежде чем ответить на вопрос, я хотел бы немного рассказать об измерении производительности командлетов PowerShell. Собственный PowerShell очень хорош в потоковых объектах и, следовательно, может сэкономить много памяти, если потоковая передача выполняется правильно ( не назначать поток переменной или использовать скобки ). PowerShell также может вызывать практически все существующие .Net методы (например, Add()
) и такие технологии, как LINQ .
Обычный способ измерения производительностиКоманда:
(Measure-Command {<myCommand>}).TotalMilliseconds
Если вы используете это в собственных потоковых командлетах PowerShell, они, кажется, не очень хорошо работают по сравнению с операторами и командами dotnet. Часто делается вывод, что, например, LINQ превосходит собственные команды PowerShell более чем в сто раз. Причина этого в том, что LINQ реагирует и использует отложенное (ленивое) выполнение: оно говорит, что оно выполнило свою работу, но на самом деле делает это в тот момент, когда вам нужен какой-либо результат (кроме того, он кэширует множество результатов, что проще всего). исключить из теста путем запуска нового сеанса), когда Native PowerShell довольно проактивен: он передает любой разрешенный элемент немедленно обратно в конвейер, и любой следующий командлет (например, Export-Csv
) может затем завершить элемент и освободить его из памяти.
Другими словами, если у вас медленный ввод (см .: Защита собственной PowerShell ) или у вас есть большой объем данных для обработки (например, больше, чем доступно физической памяти), это может быть лучше и прощеиспользовать подход Native PowerShell.
В любом случае, если вы сравниваете какие-либо результаты, вам следует протестировать их на практике и тестировать их полностью, а не только на данных, которые уже доступны в памяти.
Создание списка
Я согласен, что использование метода Add()
в списке намного быстреепри использовании +=
, который объединяет новый элемент с текущим массивом, а затем переназначает его обратно в массив.
Но, опять же, оба подхода останавливают конвейер, поскольку они собирают все данные в памяти, где выможет быть, лучше промежуточно выпустить результат на диск.
HashTables
Вероятно, вы найдете наибольшее улучшение производительности при использовании хеш-таблицы, так как они оптимизированы для двоичного поиска .
Поскольку требуется сравнениедве коллекции друг к другу, вы не можете передавать оба потока, но, как было объяснено, было бы лучше и проще всего использовать 1 хеш-таблицу для одной стороны и сравнивать ее с каждым элементом в потоке на другой стороне, а также потому, что вы хотите сравнитьAllData
для каждой из других таблиц лучше всего индексировать эту таблицу в память (в форме хеш-таблицы).
Вот как я бы это сделал:
$Main = @{}
ForEach ($Item in $All) {
$Main[$Item.EmployeeNumber] = @{MainName = $Item.Name; MainDomain = $Item.Domain}
}
ForEach ($Name in 'AAA', 'BBB', 'CCC', 'DDD') {
Import-Csv "C:\$Name.csv" | Where-Object {$Main.ContainsKey($_.EmployeeNumber)} | ForEach-Object {
[PSCustomObject](@{EmployeeNumber = $_.EmployeeNumber; Name = $_.Name; Domain = $_.Domain} + $Main[$_.EmployeeNumber])
} | Export-Csv "C:\Output$Name.csv"
}
Приложение
На основании комментария (и дубликатов в списках) выясняется, что на самом деле запрашивается объединение для всех ключей, а не только для EmployeeNumber
,Для этого вам нужно объединить соответствующие ключи (разделенные разделителем, который , а не используется в данных) и использовать его в качестве ключа для хэш-таблицы.
Не в вопросе, а из комментарияПоявляется также, что ожидается полное соединение. Для части с правым соединением это можно сделать, вернув нужный объект в случае, если в основной таблице не найдено совпадений ($Main.ContainsKey($Key)
). Для части с левым соединением это сложнее, так как вам нужно будет отследить ($ InnerMain), какие элементы в main уже сопоставлены, и вернуть оставшиеся элементы в конце:
$Main = @{}
$Separator = "`t" # Chose a separator that isn't used in any value
ForEach ($Item in $All) {
$Key = $Item.EmployeeNumber, $Item.Name, $Item.Domain -Join $Separator
$Main[$Key] = @{MainEmployeeNumber = $Item.EmployeeNumber; MainName = $Item.Name; MainDomain = $Item.Domain} # What output is expected?
}
ForEach ($Name in 'AAA', 'BBB', 'CCC', 'DDD') {
$InnerMain = @($False) * $Main.Count
$Index = 0
Import-Csv "C:\$Name.csv" | ForEach-Object {
$Key = $_.EmployeeNumber, $_.Name, $_.Domain -Join $Separator
If ($Main.ContainsKey($Key)) {
$InnerMain[$Index] = $True
[PSCustomObject](@{EmployeeNumber = $_.EmployeeNumber; Name = $_.Name; Domain = $_.Domain} + $Main[$Key])
} Else {
[PSCustomObject](@{EmployeeNumber = $_.EmployeeNumber; Name = $_.Name; Domain = $_.Domain; MainEmployeeNumber = $Null; MainName = $Null; MainDomain = $Null})
}
$Index++
} | Export-Csv "C:\Output$Name.csv"
$Index = 0
ForEach ($Item in $All) {
If (!$InnerMain[$Index]) {
$Key = $Item.EmployeeNumber, $Item.Name, $Item.Domain -Join $Separator
[PSCustomObject](@{EmployeeNumber = $Null; Name = $Null; Domain = $Null} + $Main[$Key])
}
$Index++
} | Export-Csv "C:\Output$Name.csv"
}
Join-Объект
Просто к вашему сведению, я внес несколько изменений в Join-Object
командлет (использование и установка очень просты, см .: В Powershell, как лучше объединить две таблицы в одну? ). Я сделал несколько небольших улучшений, в том числе более простую смену нескольких соединений, которые могут пригодиться для запроса, как этот. Хотя у меня до сих пор нет полного понимания того, что именно вы ищете (и у меня есть небольшие вопросы, такие как: как могут домены отличаться в столбце домена, если это выдержка из одного конкретного домена?).
Я беру общее описание" В частности, поиск объекта AD для 4 доменов и сохранение их в переменной для сравнения атрибутов " в качестве ведущего. Здесь я предполагаю, что $AllMainFile
на самом деле является просто промежуточной таблицей, существующей вне конкатенации всех соответствующих таблиц (и не обязательно, но просто сбивающей с толку, поскольку она может содержать типы дубликатов employeenumbers
из того же домена иemployeenumbers
с других доменов). Если это правильно, вы можете просто опустить эту таблицу, используя командлет Join-Object
:
$AAA = ConvertFrom-Csv @'
EmployeeNumber,Name,Domain
Z001,ABC,Domain1
Z002,DEF,Domain2
Z003,GHI,Domain3
'@
$BBB = ConvertFrom-Csv @'
EmployeeNumber,Name,Domain
Z001,ABC,Domain1
Z002,JKL,Domain2
Z004,MNO,Domain4
'@
$CCC = ConvertFrom-Csv @'
EmployeeNumber,Name,Domain
Z005,PQR,Domain2
Z001,ABC,Domain1
Z001,STU,Domain2
'@
$DDD = ConvertFrom-Csv @'
EmployeeNumber,Name,Domain
Z005,VWX,Domain4
Z006,XYZ,Domain1
Z001,ABC,Domain3
'@
$AAA | FullJoin $BBB -On EmployeeNumber -Discern AAA |
FullJoin $CCC -On EmployeeNumber -Discern BBB |
FullJoin $DDD -On EmployeeNumber -Discern CCC,DDD | Format-Table
Результат:
EmployeeNumber AAAName AAADomain BBBName BBBDomain CCCName CCCDomain DDDName DDDDomain
-------------- ------- --------- ------- --------- ------- --------- ------- ---------
Z001 ABC Domain1 ABC Domain1 ABC Domain1 ABC Domain3
Z001 ABC Domain1 ABC Domain1 STU Domain2 ABC Domain3
Z002 DEF Domain2 JKL Domain2
Z003 GHI Domain3
Z004 MNO Domain4
Z005 PQR Domain2 VWX Domain4
Z006 XYZ Domain1