Sql Powershell StringBuilder性能差?

Sql Powershell StringBuilder性能差?,sql,string,powershell,sqlite,stringbuilder,Sql,String,Powershell,Sqlite,Stringbuilder,我正在尝试构建查询,以便能够将大量数据插入sqlite3表中。我已经尝试了几种方法来实现这一点,包括能够轻松地获取数据表并插入它的方法。即使只有10000条记录,运行也需要将近40分钟,我不知道为什么 我的另一个选择是构建一个查询,并使用另一个方法(如Invoke-Sqlcmd)执行它。我尝试过用StringBuilder来实现这一点,但构建字符串需要2分钟以上的时间。就像我说的,它只有10000条记录,所以从我准备的记录来看,最多只需要10-15秒。考虑到我至少有几百万条记录要导入,我真的需要

我正在尝试构建查询,以便能够将大量数据插入sqlite3表中。我已经尝试了几种方法来实现这一点,包括能够轻松地获取数据表并插入它的方法。即使只有10000条记录,运行也需要将近40分钟,我不知道为什么

我的另一个选择是构建一个查询,并使用另一个方法(如Invoke-Sqlcmd)执行它。我尝试过用StringBuilder来实现这一点,但构建字符串需要2分钟以上的时间。就像我说的,它只有10000条记录,所以从我准备的记录来看,最多只需要10-15秒。考虑到我至少有几百万条记录要导入,我真的需要这个来加快速度

这是我正在使用的代码。我是不是错过了什么

$sb=[System.Text.StringBuilder]::new()
$sb.追加行(“开始交易”)
foreach($documents中的文档){
$null=$sb.AppendLine(“在文档中插入或忽略(DocId、submit、docno、Tray、Pieceno、CreateDate、Account、AccName、AccAddr1、AccAddr2、AccAddr3、AccAddr4、AccCity、AccState、AccZip、BCDP、条形码id、ServTypeID、Mailerid、SerialNo、Sys_Name、Sys_Addr1、Sys_Addr2、Sys_Addr3、Sys_Addr4、Sys_City、Sys_State、Sys_-Zip)”);
$null=$sb.AppendLine(“值($($document.DocId),$($document.Submid),$($document.docno),$($document.Tray),$($document.Pieceno),$($document.CreateDate),$($document.AccName),$($document.acaddr1),$($document.acaddr2),$($document.acaddr3),$($document.acaddr4),$($document.accity),'$($document.AccState)“$”($document.AccZip)“$”($document.BCDP)“$”($document.ServTypeID)“$”($document.Mailerid)“$”($document.SerialNo)“$($document.Sys\u Name)“$($document.Sys\u Addr1)“$”($document.Sys\u Addr2)“,$($document.Sys\u Addr3)”,$($document.Sys\u Addr4)“$($document.Sys\u-City)”,$($document.Sys\u-State)“,”$($document.Sys_-Zip)“)
}
$sb.追加行(“提交”)
$query=$sb.ToString();
#调用SqliteQuery$ref_db$query#对此进行注释,因为我甚至还没有尝试插入,因为StringBuilder还没有进行足够的优化。
在本例中,$documents是一个通用对象,包含INSERT语句中的每个字段。大多数字段都填充有字符串,其中一些字段为空


#编辑:我正在Powershell ISE中运行此程序,设置了断点,这会导致性能问题吗?

您是否尝试使用管道和运算符使用Powershell方式

$sb = ($Documents | Foreach { "BEGIN TRANSACTION" }  {
          "INSERT or IGNORE into documents(DocId,Submid,docno,Tray,Pieceno,CreateDate,Account,AccName,AccAddr1,AccAddr2,AccAddr3,AccAddr4,AccCity,AccState,AccZip,BCDP,BarcodeID,ServTypeID,Mailerid,SerialNo,Sys_Name,Sys_Addr1,Sys_Addr2,Sys_Addr3,Sys_Addr4,Sys_City,Sys_State,Sys_Zip)"
          "VALUES('$($_.DocId)','$($_.Submid)','$($_.docno)','$($_.Tray)','$($_.Pieceno)','$($_.CreateDate)','$($_.Account)','$($_.AccName)','$($_.AccAddr1)','$($_.AccAddr2)','$($_.AccAddr3)','$($_.AccAddr4)','$($_.AccCity)','$($_.AccState)','$($_.AccZip)','$($_.BCDP)','$($_.BarcodeID)','$($_.ServTypeID)','$($_.Mailerid)','$($_.SerialNo)','$($_.Sys_Name)','$($_.Sys_Addr1)','$($_.Sys_Addr2)','$($_.Sys_Addr3)','$($_.Sys_Addr4)','$($_.Sys_City)','$($_.Sys_State)','$($_.Sys_Zip)')"
    } { "COMMIT" }) -Join [Environment]::NewLine
或者(由于
Foreach
语句通常比
Foreach对象
cmdlet快一点):


在进入问题的
StringBuilder
方面之前,让我们先看看SQLite:

如果将SQL更改为提交一个多行
INSERT
语句,而不是像现在这样提交10000个单独的
INSERT
,我希望您能看到处理时间上的差异—换句话说:

$null = $sb.AppendLine("INSERT or IGNORE into documents(DocId,Submid,docno,Tray,Pieceno,CreateDate,Account,AccName,AccAddr1,AccAddr2,AccAddr3,AccAddr4,AccCity,AccState,AccZip,BCDP,BarcodeID,ServTypeID,Mailerid,SerialNo,Sys_Name,Sys_Addr1,Sys_Addr2,Sys_Addr3,Sys_Addr4,Sys_City,Sys_State,Sys_Zip)")
$null = $sb.AppendLine("VALUES")

foreach ($document in $documents) {
  # Add separate value tuple for each document, add trailing `,`
  $null = $sb.AppendLine("('$($document.DocId)','$($document.Submid)','$($document.docno)','$($document.Tray)','$($document.Pieceno)','$($document.CreateDate)','$($document.Account)','$($document.AccName)','$($document.AccAddr1)','$($document.AccAddr2)','$($document.AccAddr3)','$($document.AccAddr4)','$($document.AccCity)','$($document.AccState)','$($document.AccZip)','$($document.BCDP)','$($document.BarcodeID)','$($document.ServTypeID)','$($document.Mailerid)','$($document.SerialNo)','$($document.Sys_Name)','$($document.Sys_Addr1)','$($document.Sys_Addr2)','$($document.Sys_Addr3)','$($document.Sys_Addr4)','$($document.Sys_City)','$($document.Sys_State)','$($document.Sys_Zip)'),")
}
    
# trim trailing newline + comma on last insert value before adding COMMIT statement
$query = $sb.ToString().TrimEnd("`r`n,") + "`r`nCOMMIT"
Invoke-SqliteQuery $ref_db $query

通过避免可扩展字符串,而使用
$sb.AppendFormat()
,可以实现一个极小的优化,即:

$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine("BEGIN TRANSACTION")
$null = $sb.AppendFormat('VALUES ({0}, {2}, ...)', $doc.DocId, $doc.SubmId, ...).AppendLine()
…但这可能不是问题所在

在Windows PowerShell中,只要字符串大小超过.NET Framework中大型对象堆缓存(85Kb)的阈值,字符串操作(无论是通过直接连接还是通过字符串生成)就会发生一些错误

在.NET Core中似乎不会出现这种情况,因此升级到较新版本的PowerShell(如PowerShell 7)可能会一起消除此问题

如果您需要以Windows PowerShell为目标,您可能只需要将SQL脚本直接写入磁盘,然后使用
调用SqliteQuery-InputFile

$scriptFile = New-Item import.sql

try{
  $fileWriter = $scriptFile.CreateText()
  $fileWriter.WriteLine("INSERT or IGNORE into documents(DocId,Submid,docno,Tray,Pieceno,CreateDate,Account,AccName,AccAddr1,AccAddr2,AccAddr3,AccAddr4,AccCity,AccState,AccZip,BCDP,BarcodeID,ServTypeID,Mailerid,SerialNo,Sys_Name,Sys_Addr1,Sys_Addr2,Sys_Addr3,Sys_Addr4,Sys_City,Sys_State,Sys_Zip)")

  foreach ($document in $documents) {
    $fileWriter.WriteLine("VALUES")
    $fileWriter.WriteLine("('$($document.DocId)','$($document.Submid)','$($document.docno)','$($document.Tray)','$($document.Pieceno)','$($document.CreateDate)','$($document.Account)','$($document.AccName)','$($document.AccAddr1)','$($document.AccAddr2)','$($document.AccAddr3)','$($document.AccAddr4)','$($document.AccCity)','$($document.AccState)','$($document.AccZip)','$($document.BCDP)','$($document.BarcodeID)','$($document.ServTypeID)','$($document.Mailerid)','$($document.SerialNo)','$($document.Sys_Name)','$($document.Sys_Addr1)','$($document.Sys_Addr2)','$($document.Sys_Addr3)','$($document.Sys_Addr4)','$($document.Sys_City)','$($document.Sys_State)','$($document.Sys_Zip)')")
    $fileWriter.WriteLine("")
  }
  $fileWriter.WriteLine("COMMIT")
}
finally{
  $fileWriter.Close()
}    

Invoke-SqliteQuery $ref_db -InputFile import.sql

这实际上是stringbuilder性能吗?或者更确切地说是PSqlite性能?这可能是因为pssqlite试图填充结果集,我没有看到非查询选项(?)我建议构建一个一次插入多条记录的SQL语句。我总是使用存储过程,而不是用户提供的数据。我编辑我的问题是因为它有误导性。我甚至没有运行我使用StringBuilder创建的查询,因为它太慢,无法满足我的需要。构建查询f花费的时间太长。我可能需要改写我的问题。我有两个性能问题:1.使用PSSQLite直接插入我的查询结果(已经是DataTable格式)时2.当通过StringBuilder手动构建查询时。我甚至还没有运行此查询,因为我无法优化StringBuilder,使其无法运行forever@fudge您在问题中声称需要2分钟-如果您想要更快的测试,请尝试使用1000个文档。让我们知道
$documents
来自哪里?我只是看到了与这里应该可以做到的相比糟糕的表现:我认为这可能只是我正在尝试做的事情的现实。这可能只是一个缓慢的过程operation@fudge如果你向下滚动到该页面的底部,你会发现它实际上引用了我的话,指出只有当字符串长度超过某个le时才会发生这种情况波长(似乎是85KB的大对象堆缓存阈值).
Measure Script
可能会帮助您确定这是否也是您所看到的,这就是我略读这篇文章所得到的!!是的,这正是问题所在。我可以通过升级到PS v7来解决这个问题。时间从20分钟到2秒。我怎么能相信您呢?也许我会标记您的答案并编辑它包括这些细节。
$scriptFile = New-Item import.sql

try{
  $fileWriter = $scriptFile.CreateText()
  $fileWriter.WriteLine("INSERT or IGNORE into documents(DocId,Submid,docno,Tray,Pieceno,CreateDate,Account,AccName,AccAddr1,AccAddr2,AccAddr3,AccAddr4,AccCity,AccState,AccZip,BCDP,BarcodeID,ServTypeID,Mailerid,SerialNo,Sys_Name,Sys_Addr1,Sys_Addr2,Sys_Addr3,Sys_Addr4,Sys_City,Sys_State,Sys_Zip)")

  foreach ($document in $documents) {
    $fileWriter.WriteLine("VALUES")
    $fileWriter.WriteLine("('$($document.DocId)','$($document.Submid)','$($document.docno)','$($document.Tray)','$($document.Pieceno)','$($document.CreateDate)','$($document.Account)','$($document.AccName)','$($document.AccAddr1)','$($document.AccAddr2)','$($document.AccAddr3)','$($document.AccAddr4)','$($document.AccCity)','$($document.AccState)','$($document.AccZip)','$($document.BCDP)','$($document.BarcodeID)','$($document.ServTypeID)','$($document.Mailerid)','$($document.SerialNo)','$($document.Sys_Name)','$($document.Sys_Addr1)','$($document.Sys_Addr2)','$($document.Sys_Addr3)','$($document.Sys_Addr4)','$($document.Sys_City)','$($document.Sys_State)','$($document.Sys_Zip)')")
    $fileWriter.WriteLine("")
  }
  $fileWriter.WriteLine("COMMIT")
}
finally{
  $fileWriter.Close()
}    

Invoke-SqliteQuery $ref_db -InputFile import.sql