gpt4 book ai didi

powershell - 在通过管道之前对数据进行批处理

转载 作者:行者123 更新时间:2023-12-02 22:41:52 25 4
gpt4 key购买 nike

我有一堆文件共享,上面有数百万个文件/文件夹。我正在使用 gci -Recurse 获取共享目录/文件的完整列表,我需要从该 gci 加载几条信息到 SQL Server 以进行额外分析。我用来获取数据的命令是:

gci $SharePath -Recurse | select FullName, Attributes, Length, CreationTimeUtc, LastAccessTimeUtc, LasWriteTimeUtc

现在我可以按照 Microsoft's Write-SqlTableData documentation page 上的选项 3 中的建议,使用推荐的语法将其通过管道传输到 Write-SQLTableData 以强制执行批量插入。 ,像这样:

$Params = @{
ServerInstance = 'sqlservername'
DatabaseName = 'databasename'
SchemaName = 'dbo'
}
,(gci $SharePath -Recurse | select FullName, Attributes, Length, CreationTimeUtc, LastAccessTimeUtc, LasWriteTimeUtc) | Write-SqlTableData @Params -TableName 'Table1'

但是,这样做的结果是,gci 需要几个小时才能完成,没有任何反馈,并且会占用大量内存,并使我的机器慢得像爬行一样,最后将所有数据转储到 SQL。如果我不使用 ,( 和匹配的 ),数据会在生成时移动到 SQL,但是 SQL 服务器会受到数百万个单独插入的冲击。

我正在寻找的是使用管道的中间答案。我知道我可以将 gci 结果存储在变量 $gciresults 中,然后使用 $gciresults[0..999] 一次将 1000 行传递给 SQL,依此类推,但我正在尝试利用管道,因此不会占用太多内存。理想情况下,会有一些我称之为 batching-cmdlet 的 cmdlet,它允许我将传入的数据拆分成一口大小的 block ,而无需先将其全部存储在内存中,如下所示:

gci ... | select FullName, ... | batching-cmdlet -batchsize 1000 | Write-SqlTableData @Params -TableName 'Table1'

搜索此类 cmdlet 未成功。有没有人想到我可以如何实现这一目标?

最佳答案

使用@mklement0 在他接受的答案中概述的框架,我编写了以下 Split-PipelineData cmdlet,它接受管道输入并以用户可定义的批处理将其传递到下线。请注意,事实证明这与@mklement0 链接的帖子中的函数非常相似,但是我还添加了使用 write-progress 报告进度的功能。

<#
.Synopsis
Takes pipeline objects one at a time and sends them on in batches.
.DESCRIPTION
Takes pipeline objects one at a time and sends them on in batches. Allows user selectable values for
batch size and feedback options.
#>
Function Split-PipelineData
{
[CmdletBinding(DefaultParameterSetName='Default')]
Param
(
# PipelineData
[Alias('PipelineData')]
[Parameter(ParameterSetName='Default',Mandatory=$true,ValueFromPipeline=$true,Position=0)]
[Parameter(ParameterSetName='Progress',Mandatory=$true,ValueFromPipeline=$true,Position=0)]
$InputObject,

# Batch size for sending on to the pipeline
[Parameter(ParameterSetName='Default',Mandatory=$false)]
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$BatchSize=1000,

# If set, Progress will use Write-Progress to display progress information
[Parameter(ParameterSetName='Progress',Mandatory=$true)]
[switch]$Progress,

# Passthru to Write-Progress ID parameter
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$ProgressID=0,

# Passthru to Write-Progress ParentID parameter
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$ProgressParentID=-1,

# Passthru to Write-Progress Activity parameter. Default is 'Batching pipeline data'.
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$ProgressActivity=$null,

# Report progress after this many records. Defaults to same as BatchSize
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$ProgressBatchSize=$null,

# Total Record count (if known) to be used in progress
[Parameter(ParameterSetName='Progress',Mandatory=$false)]
[int]$TotalRecords=$null
)

Begin
{
$Batch = [System.Collections.Generic.Queue[pscustomobject]]::new($BatchSize)
[int64]$RecordCounter = 0
If ($Progress)
{
$ProgressParams = @{
Activity = If ($ProgressActivity) {$ProgressActivity} Else {'Batching pipeline data'}
Status = ''
ID = $ProgressID
ParentID = $ProgressParentID
PercentComplete = -1
}
If ($ProgressBatchSize -in $null,0) {$ProgressBatchSize = $BatchSize}
}
}
Process
{
$RecordCounter++

#Add record to batch
$Batch.Enqueue($_)

#Report progress if necessary
If ($Progress -and $RecordCounter % $ProgressBatchSize-eq 0)
{
If ($TotalRecords)
{
$ProgressParams.Status = "Piping record $RecordCounter/$TotalRecords"
$ProgressParams.PercentComplete = [int](100*($RecordCounter/$TotalRecords))
}
Else
{
$ProgressParams.Status = "Piping record $RecordCounter"
}
Write-Progress @ProgressParams
}

#Pass batch on if it has reached its threshhold
if ($Batch.Count -eq $BatchSize)
{
,($Batch)
$Batch.Clear() # start next batch
}
}
End
{
#Report final progress if necessary
If ($Progress)
{
If ($TotalRecords)
{
$ProgressParams.Status = "Piping record $RecordCounter/$TotalRecords"
$ProgressParams.PercentComplete = [int](100)
}
Else
{
$ProgressParams.Status = "Piping record $RecordCounter"
}
Write-Progress @ProgressParams
}

#Pass remaining records on and clear variable
,($Batch)
$Batch.Clear()
Remove-Variable Batch

#Clear progress bars if necessary
If ($Progress)
{
$ProgressParams.Activity = 'Completed'
If ($ProgressParams.ContainsKey('Status')) {$ProgressParams.Remove('Status')}
Write-Progress @ProgressParams -Completed
}
}
}

关于powershell - 在通过管道之前对数据进行批处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61333235/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com