我在调用 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 $_ }
- 使用SubExpression operator在你的脚本中(注意 $ 在某些情况下是可选的 Is "Dollar-sign" optional in powershell "$()"? )
(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/