我将假设公司编号、交易编号、交易日期和交易价值是保证每行值的唯一标识符。换句话说,它们将构成 SQL 表中的一个键。坦率地说,我的假设是事务值实际上不是密钥的一部分,而是两个文件之间的数据一致性检查。如果这些假设中的任何一个不正确,则此解决方案可能不适合您。但是,如果是这种情况,那么您的问题可能是不确定的。也就是说,您的数据文件可能没有足够的信息来实际解决问题。
Compare-Object 在这里无法正常工作。这是一个非常不稳定的命令,在大多数应用程序中表现不佳。
最简单的答案可能是“将您的数据文件加载到 SQL 数据库中并运行查询”。最终,这可能是您的最佳选择,即使您正在考虑使用 SQLite 数据库。
但是,假设您必须使用 PowerShell 执行此操作。最简单的方法是采取死而简单的蛮力方法:
#File 1:
$LoanTransactions = Import-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Test_Store_Loans_Module.txt
#File 2:
$LoanExtract = Import-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\loans_extract.csv
# Find all transactions to save to Extract1
$Extract1 = foreach ($Tran in $LoanTransactions) {
foreach ($Extract in $LoanExtract) {
if (
($Tran.File1Field1 -eq $Extract.File2Field4) -and
($Tran.File1Field8 -eq $Extract.File2Field1) -and
($Tran.File1Field11 -eq $Extract.File2Field10) -and
($Tran.File1Field7 -eq $Extract.File2Field7) -and
($Extract.File2Field2 -notin ('ABC','DEF')) -and
($Extract.File2Field3 -ne '1234')
) {
$Tran
}
}
}
# Find all transactions not in Extract1 and save them to Extract2
$Extract2 = foreach ($Tran in $LoanTransactions) {
$FoundInExtract1 = $false
foreach ($Extract in $Extract1) {
if (
($Tran.File1Field1 -eq $Extract.File2Field4) -and
($Tran.File1Field8 -eq $Extract.File2Field1) -and
($Tran.File1Field11 -eq $Extract.File2Field10) -and
($Tran.File1Field7 -eq $Extract.File2Field7)
) {
$FoundInExtract1 = $true
}
}
if (-not $FoundInExtract1) {
$Tran
}
}
$Extract1 | Where-Object $ConditionsToMeet | Export-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Extract1.txt -NoTypeInformation
$Extract2 | Where-Object $ConditionsNotMet | Export-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Extract2.txt -NoTypeInformation
通过使用哈希表作为索引来加快处理速度,您可能会更聪明。基本上,我们要做的是将$LoadExtract 加载到嵌套的哈希表中。哈希表使 非常 快速键查找。构建表应该相对较快。使用主键中有 6 个字段的本地数据库表,处理 175,000 条记录大约需要 12 秒,但您的里程可能会有所不同。这里有很多变数。
一旦构建完成,脚本就可以很容易地在 File2 中针对 File1 中的值进行查找。代码会有点难看,但它的性能应该和 PowerShell 差不多。
接下来要记住的是Import-Csv 将所有内容都导入为纯文本。这意味着重要的格式包括尾随空格。此外,哈希表是数据类型感知的。 $hash[1234] 与 $hash['1234'] 不同。如果数据类型和数据值不匹配完全,您将无法正确进行查找,因此您需要非常确定您用于键的字段完全相同。您唯一不必担心的是区分大小写。其他一切都很重要,因为没有隐式数据转换。
请注意,在下面的代码中,我不小心交换了交易日期和交易价值。这实际上并不会影响它的功能,但它会使它变得更加混乱。我责怪真正糟糕的字段名称。
首先,我们将从文件 2 中设置哈希表:
#File 1:
$LoanTransactions = Import-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Test_Store_Loans_Module.txt
#File 2:
$LoanExtract = Import-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\loans_extract.csv
#Hash table to be used for Loan Extract lookups
$LoanExtractTable = @{}
#Build the loan extract table nested hash table
$LoanExtract | ForEach-Object {
# Company Number
if (!$LoanExtractTable.ContainsKey($_.File2Field4)) {
$LoanExtractTable[$_.File2Field4] = @{}
}
# Transaction Number
if (!$LoanExtractTable[$_.File2Field4].ContainsKey($_.File2Field1)) {
$LoanExtractTable[$_.File2Field4][$_.File2Field1] = @{}
}
# Transaction Date
if (!$LoanExtractTable[$_.File2Field4][$_.File2Field1].ContainsKey($_.File2Field7)) {
$LoanExtractTable[$_.File2Field4][$_.File2Field1][$_.File2Field7] = @{}
}
# Transaction Value
# This is the last key, so we save our data here.
if (!$LoanExtractTable[$_.File2Field4][$_.File2Field1][$_.File2Field7].ContainsKey($_.File2Field10)) {
$LoanExtractTable[$_.File2Field4][$_.File2Field1][$_.File2Field7][$_.File2Field10] = $_
}
}
所以,如果我们有一个公司编号57,交易编号为123456,交易日期为2019-11-30,交易价值为123.45,您可以这样查找:
$LoanExtractTable['57']['123456']['2019-11-30']['123.45']
再次注意,这里的一切都是明确的字符串。格式对日期和货币价值很重要。然而,在下面的代码中,我们需要返回ContainsKey() 并再次嵌套 if 语句,因为我们需要查找记录的存在。如果感觉我们正在构建 SQL 样式索引并进行连接,那是因为我们基本上是。
现在,我们可以遍历交易文件了。
foreach ($LoanTransaction in $LoanTransactions) {
$ValidExtract = $false
# Company Number
if ($LoanExtractTable.ContainsKey($LoanTransaction.File1Field1)) {
# Transaction Number
if ($LoanExtractTable[$LoanTransaction.File1Field1].ContainsKey($LoanTransaction.File1Field8)) {
# Transaction Date
if ($LoanExtractTable[$LoanTransaction.File1Field1][$LoanTransaction.File1Field8].ContainsKey($LoanTransaction.File1Field7)) {
# Transaction Value
if ($LoanExtractTable[$LoanTransaction.File1Field1][$LoanTransaction.File1Field8][$LoanTransaction.File1Field7].ContainsKey($LoanTransaction.File1Field11)) {
# It's in the LoanExtract file! Now we can do our additional data checks
if (
$LoanExtractTable[$LoanTransaction.File1Field1][$LoanTransaction.File1Field8][$LoanTransaction.File1Field7][$LoanTransaction.File1Field11].File2Field2 -notin @('ABC','DEF')
-and $LoanExtractTable[$LoanTransaction.File1Field1][$LoanTransaction.File1Field8][$LoanTransaction.File1Field7][$LoanTransaction.File1Field11].File2Field3 -ne '1234')
) {
# It's valid to export to Extract1.txt
$ValidExtract = $true
$LoanTransaction | Export-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Extract1.txt -Append -NoTypeInformation
}
}
}
}
}
if ($ValidExtract -eq $false) {
$LoanTransaction | Export-Csv \\774512-LRBSPT01\********$\uardata\rt1\BankRec\Test\step1\Extract2.txt -Append -NoTypeInformation
}
}
这里肯定有优化和改进的空间。我不知道您的数据是什么样的,但我不得不认为上面字段的顺序非常糟糕。我认为交易日期、公司编号、交易编号、交易价值会更好。
此外,您可以轻松地将 File2Field2 和 File2Field3 的测试移动到您正在构建哈希表的位置,但我不能确定您正在做的所有事情。
写出数据的方式也不是非常理想,但它可能比迭代两次或使用+= 的数组连接要好。至少这样可以使用磁盘缓冲区。
上面的代码不简单,但它与我想的一样简单,无需让 PowerShell 为 File 1 的每一行迭代 File2(即,做一个笛卡尔积)如果两个文件都很大,可能会非常慢。