You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PowerShell脚本:SQL脚本循环执行遇错如何跳过并续行?

实现单个SQL脚本执行失败后继续循环的方案

当然可以通过嵌套try/catch块来实现这个需求,这也是最直接可靠的方式。你的外层try/catch负责处理整个脚本的顶级错误,但要让单个SQL脚本执行失败不中断循环,必须把每个脚本的完整执行逻辑单独包裹在独立的try/catch块里。

核心思路

把处理单个SQL脚本的所有逻辑(读取文件、处理内容、执行SQL语句)放进try块中,当这个脚本的任何步骤抛出异常时,catch块会捕获错误、记录信息,然后循环会自动继续处理下一个脚本,不会中断整个循环流程。

修改后的代码示例

foreach ($sqlScript in Get-ChildItem -path "$pathToScripts" -Filter *.sql | Sort-Object) {
    Write-Host "Running Script " $sqlScript.Name
    try {
        # 执行当前SQL脚本的完整逻辑
        switch ($removeComments) {
            $true {
                (Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -replace '(?s)/\*.*?\*/', " " -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' | 
                ForEach-Object {
                    $SqlCmd.CommandText = $_.Trim()
                    $reader = $SqlCmd.ExecuteNonQuery()
                }
            }
            $false {
                (Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String) -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' | 
                ForEach-Object {
                    $SqlCmd.CommandText = $_.Trim()
                    $reader = $SqlCmd.ExecuteNonQuery()
                }
            }
        }
        Write-Host "Successfully executed script: $($sqlScript.Name)"
    }
    catch {
        # 捕获当前脚本的执行错误,输出警告并继续
        Write-Warning "Failed to execute script: $($sqlScript.Name). Error details: $_"
        # 可选:将错误写入日志文件,方便后续排查
        # Add-Content -Path ".\sql_execution_errors.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Failed script: $($sqlScript.Name) | Error: $_"
    }
}

额外优化:减少代码冗余

你会发现switch的两个分支逻辑高度重复,只有移除注释的步骤不同。可以把重复的SQL执行逻辑提取成一个函数,提升代码的可维护性:

# 定义复用的SQL批量执行函数
function Invoke-SqlBatch {
    param(
        [Parameter(Mandatory)]
        [string]$SqlContent,
        [Parameter(Mandatory)]
        [System.Data.SqlClient.SqlCommand]$SqlCmd
    )

    # 按GO拆分语句并执行非空的SQL块
    $SqlContent -split '\r?\n\s*go\s*\r\n?' -notmatch '^\s*$' | 
    ForEach-Object {
        $SqlCmd.CommandText = $_.Trim()
        $SqlCmd.ExecuteNonQuery()
    }
}

# 主循环逻辑
foreach ($sqlScript in Get-ChildItem -path "$pathToScripts" -Filter *.sql | Sort-Object) {
    Write-Host "Running Script " $sqlScript.Name
    try {
        # 读取SQL文件内容
        $sqlContent = Get-Content $sqlScript.FullName -Encoding UTF8 | Out-String
        
        # 根据需求移除注释
        if ($removeComments) {
            $sqlContent = $sqlContent -replace '(?s)/\*.*?\*/', " "
        }

        # 执行SQL批量语句
        Invoke-SqlBatch -SqlContent $sqlContent -SqlCmd $SqlCmd
        Write-Host "Successfully executed script: $($sqlScript.Name)"
    }
    catch {
        Write-Warning "Failed to execute script: $($sqlScript.Name). Error details: $_"
        # 可选:写入错误日志
        # Add-Content -Path ".\sql_execution_errors.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Failed script: $($sqlScript.Name) | Error: $_"
    }
}

关于其他方式的说明

有人可能会问能不能用ErrorAction参数来跳过错误?但要注意:SqlCmd.ExecuteNonQuery()是.NET方法,它抛出的是终止性异常,而ErrorAction只对PowerShell的原生命令(非.NET方法)的非终止错误有效。所以对于这种场景,嵌套try/catch是唯一可靠的处理方式。

内容的提问来源于stack exchange,提问作者Andrew Lackenby

火山引擎 最新活动