【问题标题】:Faster iteration更快的迭代
【发布时间】:2018-10-07 22:44:37
【问题描述】:

我有这段代码,它是根据时间范围返回 SQL 行列表的函数的一部分。

查询本身(第一行代码)非常快。但是提取相关数据的foreach 循环需要一段时间才能完成。

我有大约 350.000 行要迭代,尽管这需要一段时间,但我想知道是否可以进行任何更改以使其更快。

$SqlDocmasterTableResuls = $this.SqlConnection.GetSqlData("SELECT DOCNUM, DOCLOC FROM MHGROUP.DOCMASTER WHERE ENTRYWHEN between '" + $this.FromDate + "' and '" + $this.ToDate + "'")

[System.Collections.ArrayList]$ListOfDocuments = [System.Collections.ArrayList]::New()

if ($SqlDocmasterTableResuls.Rows.Count)
{
    foreach ($Row in $SqlDocmasterTableResuls.Rows)
    {
        $DocProperties = @{
            "DOCNUM"      = $Row.DOCNUM
            "SOURCE"      = $Row.DOCLOC
            "DESTINATION" = $Row.DOCLOC -replace ([regex]::Escape($this.iManSourceFileServerName + ":" + $this.iManSourceFileServerPath.ROOTPATH)),
                            ([regex]::Escape($this.iManDestinationFileServerName + ":" + $this.iManDestinationFileServerPath.ROOTPATH))
        }

        $DocObj = New-Object -TypeName PSObject -Property $DocProperties
        $ListOfDocuments.Add($DocObj)
    }

    return $ListOfDocuments

【问题讨论】:

  • SQL 查询很有可能实际上只在 if/foreach 语句调用期间执行。您是否检查过 SQL 跟踪以了解实际 SQL 查询花费了多长时间?
  • @Russell 查询最多需要 10 秒...我已经调试了代码,在 foreach 中大约需要 5 分钟。坦克
  • 酷 - 要检查的另一件事是尝试使用正则表达式匹配删除 2 行以消除它们导致延迟。
  • @Russell 我已经考虑过了,但我需要这个改变。我仍然会尝试将其删除以查看结果。Tnks
  • “以后再改”往往变成“永不改”。

标签: performance powershell foreach iteration


【解决方案1】:

避免在循环中追加到数组。在变量中捕获循环数据的最佳方法是在变量中简单地收集循环输出:

$ListOfDocuments = foreach ($Row in $SqlDocmasterTableResuls.Rows) {
    New-Object -Type PSObject -Property @{
        "DOCNUM"      = $Row.DOCNUM
        "SOURCE"      = $Row.DOCLOC
        "DESTINATION" = $Row.DOCLOC -replace ...
    }
}

您不需要周围的 if 条件,因为如果表没有任何行,循环应该直接跳过它,留下一个空结果。

由于您无论如何都想返回列表,因此您甚至不需要将循环输出收集到变量中。让输出保持原样,无论如何它都会返回。

当结果不变时,还要避免在循环中重复操作。在循环之前计算一次转义的源和目标路径:

$srcPath = [regex]::Escape($this.iManSourceFileServerName + ':' + $this.iManSourceFileServerPath.ROOTPATH)
$dstPath = [regex]::Escape($this.iManDestinationFileServerName + ':' + $this.iManDestinationFileServerPath.ROOTPATH)

并在循环内使用变量$srcPath$dstPath

应该这样做:

$SqlDocmasterTableResuls = $this.SqlConnection.GetSqlData("SELECT ...")

$srcPath = [regex]::Escape($this.iManSourceFileServerName + ':' + $this.iManSourceFileServerPath.ROOTPATH)
$dstPath = [regex]::Escape($this.iManDestinationFileServerName + ':' + $this.iManDestinationFileServerPath.ROOTPATH)
foreach ($Row in $SqlDocmasterTableResuls.Rows) {
    New-Object -Type PSObject -Property @{
        'DOCNUM'      = $Row.DOCNUM
        'SOURCE'      = $Row.DOCLOC
        'DESTINATION' = $Row.DOCLOC -replace $srcPath, $dstPath
    }
}

return

【讨论】:

  • Tnks 提示。我会试一试。但是正则表达式替换我需要将它包含在循环中,因为它基于来自表的值,它永远不会相同,它是一个文件路径,我需要更改。类似于 c:\folder1\file1.txt 到 d:\folder2\file1.txt。坦克。我会让你知道结果。
  • huuuge 差异......一半时间:) tnks Ansgar。
  • 哦...而且您对循环外的 $srcPath 和 $dstPath 也是正确的...我的错误。:)
【解决方案2】:

[edit - per Ansgar Wiechers,PSCO 加速器仅适用于 ps3+。]

另一件可能有帮助的事情是将New-Object 替换为[PSCustomObject]。这通常使用起来要快一些。像这样的......

$DocObj = [PSCustomObject]$DocProperties

使用该类型加速器的另一种方法是执行 Ansgar Wiechers 在其代码示例中所做的操作,但使用加速器而不是 cmdlet。像这样……

[PSCustomObject]@{
    'DOCNUM'      = $Row.DOCNUM
    'SOURCE'      = $Row.DOCLOC
    'DESTINATION' = $Row.DOCLOC -replace $srcPath, $dstPath
    }

希望对你有帮助,

【讨论】:

  • 小费。我试试看。
  • 值得注意的是,[PSCustomObject] 类型加速器在 PowerShell v3 之前不可用。
猜你喜欢
  • 1970-01-01
  • 2021-07-31
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 2012-11-17
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多