sql-server - 在包含另一个 Invoke-SqlCmd 的循环中使用时,Invoke-SqlCmd 会生成异常

标签 sql-server powershell

我在调用 Invoke-SqlCmd 时遇到问题,因为它包含第二个 Invoke-SqlCmd 调用:

function Get-Foo
{

  $query=`
@"
WITH data AS 
(
  SELECT  1 ID, 'A' NAME
  UNION ALL
  SELECT  2 ID, 'B' NAME
  UNION ALL
  SELECT  3 ID, 'C' NAME
)
SELECT  *
FROM    data
"@

  Invoke-SqlCmd -ServerInstance "SERVER" -Database "DATABASE" -Query $query

}

function Get-Bar
{
  param
  (
    [int]$ParentId
  )

  $query=`
@"
WITH data AS 
(
  SELECT  1 ID, 'A' NAME, 1 PARENT_ID
  UNION ALL
  SELECT  2 ID, 'B' NAME, 1 PARENT_ID
  UNION ALL
  SELECT  3 ID, 'C' NAME, 2 PARENT_ID
)
SELECT  *
FROM    data
WHERE   parent_id = $ParentId
"@

  Invoke-SqlCmd -ServerInstance "SERVER" -Database "DATABASE" -Query $query

}

Get-Foo | ForEach-Object {

  Get-Bar -ParentId $_.ID

}

外循环的第一次迭代工作正常,但是当它尝试第二次迭代时,会产生异常:

Invoke-SqlCmd : The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services. At Untitled-1.ps1:18 char:3 + Invoke-SqlCmd -ServerInstance "SERVER" -Database "DATABASE ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidResult: (SERVER:PSObject) [Invoke-Sqlcmd], PSInvalidOperationExceptio n + FullyQualifiedErrorId : ExecutionFailed,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand

但是,此语法有效:

$foo = Get-Foo
$foo | ForEach-Object {

  Get-Bar

}

我猜我需要关闭第一个 Invoke-SqlCmd,但也许还有其他解决方案。

最佳答案

这与 PowerShell 管道的工作方式有关(参见 Understanding pipelines ),以及 Invoke-SqlCmd 中的限制,它不喜欢并行运行多个查询。

为了说明,让我们按如下方式重写您的示例:

function Invoke-SqlCmd
{
    write-output "aaa";
    write-output "bbb";
    write-output "ccc";
}

function Get-Foo
{
    write-host "Get-Foo started";
    Invoke-SqlCmd;
    write-host "Get-Foo finished";
}

function Get-Bar
{
    param( $value )
    write-host "Get-Bar '$value'";
}

现在运行这个脚本:

write-host "using pipelines"
Get-Foo | foreach-object { Get-Bar $_ }

给出了这个输出:

using pipelines
Get-Foo started
Get-Bar 'aaa'
Get-Bar 'bbb'
Get-Bar 'ccc'
Get-Foo finished

如果查看输出,您可以看到正在调用“Get-Bar”命令,而 Get-Foo 在管道中仍处于“事件”状态。

与此比较:

write-host "using immediate evaluation"
(Get-Foo) | foreach { Get-Bar $_ }

给出:

using immediate evaluation
Get-Foo started
Get-Foo finished
Get-Bar 'aaa'
Get-Bar 'bbb'
Get-Bar 'ccc'

在将值通过管道传输到 Get-Bar 之前执行 Get-Foo 直至完成。

您的特定错误是由于您的脚本使用多个并发管道步骤但 Invoke-SqlCmd 不支持多个并发管道步骤。幸运的是,您可以通过几种不同的方式告诉 PowerShell 使用“立即评估”,包括其他答案中提到的方法:

  • 首先将表达式分配给一个临时变量:
$foo = Get-Foo;
$foo | ForEach-Object { Get-Bar $_ }

(Get-Foo) | Foreach-Object { Get-Bar $_ }
  • 或将 Invoke-SqlCmd 包装在函数内的子表达式中。 (这有点误导,因为 Get-Foo 在管道中仍然处于事件状态但是立即评估 Invoke-SqlCmd)。
function Get-Foo
{
    write-host "Get-Foo started";
    (Invoke-SqlCmd);
    write-host "Get-Foo finished";
}

关于sql-server - 在包含另一个 Invoke-SqlCmd 的循环中使用时,Invoke-SqlCmd 会生成异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57665324/

相关文章:

sql-server - 如何使用 sqlcmd.exe 调用带有参数的存储过程?

sql - 从同一个表中具有最早值的表中选择不同的 ID 列表

powershell - 相当于Linux的powershell “mkdir -p”?

arrays - Powershell排序对象

sql-server - 将逗号分隔的字符串转换为要在 "IN"SQL 中使用的整数

sql-server - 如何运行返回数据集的存储过程

sql - PostgreSQL 中的数据透视表

json - 如何使用 PowerShell 更新 JSON 文件

powershell - 重命名项认为变量是一个路径

powershell - 生成随 secret 码