加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

在TSQL错误消息中获取文件中的行号

发布时间:2020-12-12 16:27:55 所属栏目:MsSql教程 来源:网络整理
导读:考虑下面的sql脚本 :ON ERROR EXITPRINT 'Line 3'GOPRINT 'Line 6'GOSELECT * FROM NonExistingTableGOPRINT 'Line 12'GO 使用SQLCMD运行时 sqlcmd -i MyScript.sqlLine 3Line 6Msg 208,Level 16,State 1,Server MyServer,Line 2Invalid object name 'NonExis
考虑下面的sql脚本
:ON ERROR EXIT

PRINT 'Line 3'
GO

PRINT 'Line 6'
GO

SELECT * FROM NonExistingTable
GO

PRINT 'Line 12'
GO

使用SQLCMD运行时

> sqlcmd -i MyScript.sql
Line 3
Line 6
Msg 208,Level 16,State 1,Server MyServer,Line 2
Invalid object name 'NonExistingTable'.

当您在启用了SQLCMD模式的SQL Server Management Studio中运行时,您可以获得

Line 3
Line 6
Msg 208,Line 2
Invalid object name 'NonExistingTable'.
** An error was encountered during execution of batch. Exiting.

但是当您双击错误行时,查询编辑器将跳转到有问题的行.

报告行2表示相对于批次的行号.批量由GO语句分隔.我们想要得到一个真正的第9行答案.

我也尝试过PowerShell的Invoke-Sqlcmd,但更糟糕的是,因为它根本没有检测到这样的错误(Error detection from Powershell Invoke-Sqlcmd not always working?).

有没有一个简单的方法来包装我们的sql脚本与一些帮助来获得所需的真正的错误行?

UPD:我已经更改了错误脚本,以确保它会失败确定…

解决方法

这是我想出的解决方案: https://github.com/mnaoumov/Invoke-SqlcmdEx

现在

> .Invoke-SqlcmdEx.ps1 -InputFile .MyScript.sql
Line 3
Line 6
Msg 208,Script .MyScript.ps1,Line 9
Invalid object name 'NonExistingTable'.

sqlcmd failed for script .MyScript.ps1 with exit code 1
At C:DevInvoke-SqlcmdExInvoke-SqlcmdEx.ps1:77 char:18
+             throw <<<<  "sqlcmd failed for script $InputFile with exit code $LASTEXITCODE"
    + CategoryInfo          : OperationStopped: (sqlcmd failed f...ith exit code 1:String) [],RuntimeException
    + FullyQualifiedErrorId : sqlcmd failed for script .MyScript.ps1 with exit code 1

它有一个正确的第9行输出

以防万一我也在这里列出了脚本.该脚本可能看起来像一个过分的,但它是这样写的,以便完全支持所有SQLCMD脚本功能并正确处理事务

调用-SqlcmdEx.ps1

#requires -version 2.0

[CmdletBinding()]
param
(
    [string] $ServerInstance = ".",[string] $Database = "master",[string] $User,[string] $Password,[Parameter(Mandatory = $true)]
    [string] $InputFile
)

$script:ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
function PSScriptRoot { $MyInvocation.ScriptName | Split-Path }

trap { throw $Error[0] }

function Main
{
    if (-not (Get-Command -Name sqlcmd.exe -ErrorAction SilentlyContinue))
    {
        throw "sqlcmd.exe not found"
    }

    $scriptLines = Get-Content -Path $InputFile
    $extendedLines = @()

    $offset = 0
    foreach ($line in $scriptLines)
    {
        $offset++
        if ($line -match "^s*GOs*$")
        {
            $extendedLines += `
                @(
                    "GO","PRINT '~~~ Invoke-SqlcmdEx Helper - Offset $offset'"
                )
        }

        $extendedLines += $line
    }

    $tempFile = [System.IO.Path]::GetTempFileName()

    try
    {
        $extendedLines > $tempFile

        $sqlCmdArguments = Get-SqlCmdArguments

        $ErrorActionPreference = "Continue"
        $result = sqlcmd.exe $sqlCmdArguments -i $tempFile 2>&1
        $ErrorActionPreference = "Stop"

        $offset = 0
        $result | ForEach-Object -Process `
            {
                $line = "$_"
                if ($line -match "~~~ Invoke-SqlcmdEx Helper - Offset (?<Offset>d+)")
                {
                    $offset = [int] $Matches.Offset
                }
                elseif (($_ -is [System.Management.Automation.ErrorRecord]) -and ($line -match "Line (?<ErrorLine>d+)$"))
                {
                    $errorLine = [int] $Matches.ErrorLine
                    $realErrorLine = $offset + $errorLine
                    $line -replace "Line d+$","Script $InputFile,Line $realErrorLine"
                }
                else
                {
                    $line
                }
            }

        if ($LASTEXITCODE -ne 0)
        {
            throw "sqlcmd failed for script $InputFile with exit code $LASTEXITCODE"
        }
    }
    finally
    {
        Remove-Item -Path $tempFile -ErrorAction SilentlyContinue
    }
}

function Get-SqlCmdArguments
{
    $sqlCmdArguments = `
        @(
            "-S",$ServerInstance,"-d",$Database,"-b","-r",0
        )

    if ($User)
    {
        $sqlCmdArguments += `
            @(
                "-U",$User,"-P",$Password
            )
    }
    else
    {
        $sqlCmdArguments += "-E"
    }

    $sqlCmdArguments
}

Main

UPD:@MartinSmith提供了一个整洁的想法,使用LINENO aproach.

这是使用这种方法的版本:https://github.com/mnaoumov/Invoke-SqlcmdEx/blob/LINENO/Invoke-SqlcmdEx.ps1它在每个GO语句之后基本上插入LINENO [对应行号].

但是,如果我们考虑以下脚本

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('dbo.MyFunction') AND type = 'FN')
    EXEC sp_executesql N'CREATE FUNCTION dbo.MyFunction() RETURNS int AS BEGIN RETURN 0 END'
GO
LINENO 3

ALTER FUNCTION dbo.MyFunction()
RETURNS int
AS
BEGIN
    RETURN 42
END
GO

它会失败

> sqlcmd -i MyScript.sql
Msg 111,Level 15,Line 5
'ALTER FUNCTION' must be the first statement in a query batch.
Msg 178,Line 9
A RETURN statement with a return value cannot be used in this context.

因此,LINENO方法对于在查询批次中必须是第一个的语句将不起作用.以下是此类语句的列表:http://msdn.microsoft.com/en-us/library/ms175502.aspx:CREATE DEFAULT,CREATE FUNCTION,CREATE PROCEDURE,CREATE RULE,CREATE SCHEMA,CREATE TRIGGER和CREATE VIEW.没有提到ALTER语句,但我认为规则也适用于它们

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读