You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell脚本更新SQL表异常:仅重复更新单个任务5次

问题根源分析
  1. 任务ID复用问题:原脚本用ExecuteScalar()获取tID,该方法仅返回查询结果的第一行第一列,导致所有任务都复用Tasks表中第一个启用任务的ID,后续TaskLogs的tID全为同一个值,更新时关联错误。
  2. 事件作用域陷阱:Register-ObjectEvent的-Action块在独立作用域执行,直接引用外部循环的$rid会因延迟绑定,最终所有事件触发时都使用循环最后一次的$rid值,导致重复更新同一条记录。
  3. 变量拼写错误:Action块中的$eventsubsciber拼写错误(应为$eventSubscriber),导致无法注销事件订阅,可能引发重复触发。
  4. 函数变量作用域错误:JobCompleted函数调用时传递了两个参数,但函数定义仅接受一个;且函数内部引用的$job、$rid等变量不在函数作用域内,无法正确获取对应任务信息。
  5. 数据库连接有效性问题:长期运行的脚本中,事件触发时初始创建的数据库连接可能已断开,导致更新操作失败。

修复后的脚本
$connectionstring = "Server=localhost;Database=testDB;Trusted_Connection=True;"
$jobs=@()
$testOutput="C:\Users\tarik.gacanovic\Desktop\outfile.txt"

$maxConcurrentJobs=5

# 一次性查询所有启用任务的完整信息(含ID、执行路径、参数)
$tasksQuery = @"
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT ID, ExecPath, Param1, Param2, Param3 FROM Tasks WHERE Enabled=1
"@
$tasks = Invoke-Sqlcmd -ServerInstance 'localhost' -Database 'testDB' -Query $tasksQuery

function JobCompleted($jobId, $taskId) {
    # 事件中单独创建数据库连接,避免连接失效
    $connection = New-Object System.Data.SqlClient.SqlConnection($connectionstring)
    try {
        $connection.Open()
        $job = Get-Job -Id $jobId
        if (-not $job) { return }

        # 根据任务状态设置对应参数
        switch ($job.State) {
            'Completed' {
                $status = 2
                $note = "任务执行成功"
            }
            'Failed' {
                $status = 3
                $note = "任务执行失败"
            }
            default {
                $status = 9
                $note = "状态未定义:$($job.State)"
            }
        }

        # 参数化更新语句,避免SQL注入并确保关联正确
        $updateQuery = "UPDATE TaskLogs SET Status=@Status, Finished=GETDATE(), Note=@Note WHERE ID=@JobId AND tID=@TaskId"
        $cmd = New-Object System.Data.SqlClient.SqlCommand($updateQuery, $connection)
        $cmd.Parameters.AddWithValue("@Status", $status)
        $cmd.Parameters.AddWithValue("@Note", $note)
        $cmd.Parameters.AddWithValue("@JobId", $jobId)
        $cmd.Parameters.AddWithValue("@TaskId", $taskId)
        $rowsAffected = $cmd.ExecuteNonQuery()
        "$(Get-Date): 更新TaskLogs,受影响行数:$rowsAffected" | Out-File -FilePath $testOutput -Append
    }
    catch {
        "$(Get-Date): 更新失败 - $_" | Out-File -FilePath $testOutput -Append
    }
    finally {
        $connection.Close()
    }
}

foreach($task in $tasks){
    # 控制并发任务数
    while(($jobs | Where-Object{$_.State -eq 'Running'}).Count -ge $maxConcurrentJobs){
        Start-Sleep -Milliseconds 500
    }
 
    # 通过参数传递执行路径和参数,避免依赖环境变量
    $job = Start-Job -ScriptBlock {
        param($execPath, $param1, $param2, $param3)
        & $execPath $param1 $param2 $param3
    } -ArgumentList $task.ExecPath, $task.Param1, $task.Param2, $task.Param3
    $jobs += $job
        
    # 插入TaskLogs初始记录,使用参数化查询
    $connection = New-Object System.Data.SqlClient.SqlConnection($connectionstring)
    try {
        $connection.Open()
        $status = 1
        $note = "任务启动中 - $($job.Name)"
        $insertQuery = "INSERT INTO TaskLogs(ID, tID, Status, Created, Finished, Note) VALUES(@JobId, @TaskId, @Status, GETDATE(), NULL, @Note)"
        $cmd = New-Object System.Data.SqlClient.SqlCommand($insertQuery, $connection)
        $cmd.Parameters.AddWithValue("@JobId", $job.Id)
        $cmd.Parameters.AddWithValue("@TaskId", $task.ID)
        $cmd.Parameters.AddWithValue("@Status", $status)
        $cmd.Parameters.AddWithValue("@Note", $note)
        $cmd.ExecuteNonQuery()
        "$(Get-Date): 插入TaskLogs记录" | Out-File -FilePath $testOutput -Append
    }
    catch {
        "$(Get-Date): 插入记录失败 - $_" | Out-File -FilePath $testOutput -Append
        Stop-Job -Job $job
        Remove-Job -Job $job
        continue
    }
    finally {
        $connection.Close()
    }
          
    # 注册事件,通过-MessageData传递任务ID,避免作用域陷阱
    Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
        $jobId = $EventArgs.JobStateInfo.Job.Id
        $taskId = $Event.MessageData
        JobCompleted -jobId $jobId -taskId $taskId
        Unregister-Event -SourceIdentifier $EventSubscriber.SourceIdentifier
        Remove-Job -Id $jobId
    } -MessageData $task.ID
}

关键修复点说明
  • 任务ID正确关联:一次性查询所有启用任务的ID,确保每个任务对应唯一的tID。
  • 解决作用域陷阱:通过-MessageData将任务ID传递给事件Action,从事件参数中直接获取JobID,避免引用外部循环变量。
  • 参数化SQL查询:所有数据库操作使用参数化查询,防止SQL注入并确保变量传递准确。
  • 独立数据库连接:在事件和插入操作中单独创建连接,避免长期持有连接导致的失效问题。
  • 完善错误处理:增加异常捕获,记录错误日志并清理失败任务,提升脚本稳定性。

内容的提问来源于stack exchange,提问作者Tigi Casper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:23:13