【问题标题】:PowerShell - Invoke-Sqlcmd results to Export-Csv set batch row limitPowerShell - Invoke-Sqlcmd 结果到 Export-Csv 设置批处理行限制
【发布时间】:2020-05-21 19:42:39
【问题描述】:

我正在通过 Invoke-Sqlcmd 脚本导出大约 1,000,000 行,并将其导出为大约 180MB 的 csv。

我想做的是能够将其导出到 10 100,000 个行文件中。我该怎么做,因为它似乎不是 Export-CsvInvoke-Sqlcmd 中我能找到的功能。

当前代码:

$dataSubset = "mydata"
$localFile = ("{0}.csv" -f $dataSubset)
$localPath = "my\path"
$serverInstance = "myserver.domain.com"
$database = "mydatabase"

$Query = @"
my crazy ass query
"@
$Results = Invoke-Sqlcmd -QueryTimeout 0 -ServerInstance $ServerInstance -Database $database -Query $Query
$Results | Export-csv $localPath/$localFile -NoTypeInformation

我想要mydata_1.csvmydata_2.csv等而不是单个mydata.csv

【问题讨论】:

    标签: windows powershell invoke-sqlcmd


    【解决方案1】:

    使用Select-Object cmdlet(其内置别名为select),它具有-Skip-First 参数:

    for ($i=0; $i -lt 10; $i++) {
        $localFile = ("{0}_{1}.csv" -f $dataSubset, $i)
        $Results | Select -Skip ($i * 100000) -First 100000 | Export-Csv $localPath/$localFile -NoTypeInformation
    }
    

    【讨论】:

    • 很好的指针,尽管您可能不想通过管道为每个批次发送整个 100 万个对象数组;理想情况下,您应该避免在内存中收集所有 100 万个对象。
    【解决方案2】:

    不幸的是,从 PowerShell 7.0 开始,PowerShell 中没有批处理(分块)机制。

    • This GitHub issue 建议将 -ReadCount 参数添加到 Select-Object 以启用批处理。

    现在,您必须自己实现:

    # ... 
    
    $batchSize = 10000
    $fileNdx = 0
    
    # The list to hold a batch.
    $batch = [Collections.Generic.List[object]]::new($batchSize)
    
    # The script block for exporting a single batch to a CSV.
    # Exports to .../mydata_1.csv, .../mydata_2.csv, ... 
    $sb = { 
            ++$fileNdx; 
            $batch | Export-Csv -NoTypeInformation "$localPath/mydata_$fileNdx.csv" 
          }
    
    Invoke-Sqlcmd -QueryTimeout 0 -ServerInstance $ServerInstance -Database $database -Query $Query |
      ForEach-Object -Process {
        $batch.Add($_) # add object to current batch
        if ($batch.Count -eq $batchSize) { # batch is complete
          . $sb # export
          $batch.Clear() # start new batch
        }
      } -End { 
        if ($batch.Count) { # final, incomplete batch
          . $sb # export
        }
      }
    

    请注意,您通常应该避免 $Results = Invoke-Sqlcmd ... 处理大型查询,因为它总是将整个结果集收集在内存中作为一个整体 - 而是使用 管道,及其逐个对象处理,如上所示。
    也就是说,如果您有足够的内存,一次捕获所有输入可以根据情况提高性能。

    【讨论】:

      【解决方案3】:

      我不认为 powershell 是做到这一点的方法。您不能通过 cmd 集成直接从 SQL Server 导出吗?

      无论如何,关于你的问题,另一种选择是:

      $length = $Results.length
      
      
      $items = 10;
      $stepwitch = [Math]::Ceiling($length / $items)
      
      for($i=1;$i -le $items; $i++) {
      $firstItem = ($i - 1) * $stepwitch 
      $lastItem = ($i * $stepwitch) - 1
      if ($lastItem -gt $length) {$lastItem = $length}
      echo ("FI: " + $firstItem + " LI:"  + $lastItem)
      $localFile = ("{($i)}.csv" -f $dataSubset)
      $Results[$firstItem..$lastItem] | Export-csv $localPath/$localFile -NoTypeInformation
      }
      

      (未测试,但最后类似于 Mark Arends 解决方案)

      另一种选择可能是使用 .net 方法 system.array.copy https://docs.microsoft.com/de-de/dotnet/api/system.array.copy?view=netcore-3.1

      【讨论】:

        猜你喜欢
        • 2021-10-02
        • 1970-01-01
        • 2023-03-12
        • 2016-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-08
        • 1970-01-01
        相关资源
        最近更新 更多