如何在CAST/CONVERT之前检查VARCHAR(n)格式良好的XML

gre*_*nis 5 t-sql validation casting xquery-sql

我的公司有一个记录表,其中包含一个VARCHAR(N)列,其中放置了一个应该是XML 的字符串,但事实证明它并不总是格式良好.为了对日志记录进行分析(以识别错误趋势等),我一直在使用LIKE语句.然而,这非常缓慢.

最近,我发现SQL Server支持XQuery,所以我开始玩它.我遇到的问题是我无法找到一种方法来处理CAST/CONVERT我的SELECT声明中的错误.我最接近的是需要SQL Server 2012,因为它具有该TRY_CONVERT功能,但是从2008 R2升级现在不是一个选项.

这就是我所拥有的(如果我的公司在2012年运行,这将有效):

CREATE FUNCTION IsMatch(
    @message AS VARCHAR(MAX),
    @match AS VARCHAR(MAX),
    @default AS VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @xml XML = TRY_CONVERT(XML, @message)
    DECLARE @result VARCHAR(MAX) =
        CASE WHEN @xml IS NOT NULL
             THEN CASE WHEN @xml.exist('(/FormattedMessage)[contains(.,sql:variable("@match"))]') = 1
                       THEN @match 
                       ELSE @default 
                       END
             ELSE CASE WHEN @message LIKE '%' + @match + '%'
                       THEN @match
                       ELSE @default
                       END
             END
    RETURN @result
END

GO

DECLARE @search VARCHAR(MAX) = 'a substring of my xml error message'

SELECT Error, COUNT(*) as 'Count'
FROM ( SELECT TOP 319 [LogID]
          ,[Severity]
          ,[Title]
          ,[Timestamp]
          ,[MachineName]
          ,[FormattedMessage]
          --,CAST([formattedmessage] as xml)
          ,IsMatch(@search, 'Other') as 'Error'
       FROM [MyDatabase].[dbo].[Log] (NOLOCK) ) a
GROUP BY Error
Run Code Online (Sandbox Code Playgroud)

注释CAST(或者替代地CONVERT)一旦遇到格式错误的XML,就会导致查询出错.如果我限制为TOP(N),我可以确保没有错误,并且SELECT语句工作速度非常快.我只需要一种方法来处理每行错误.

我考虑使用TRY/CATCHin IsMatch(),但不能在函数中使用.或者,要使用TRY/CATCH,我考虑了存储过程,但我无法弄清楚如何在我的SELECT子句中包含它.

Lou*_*cci 4

如果您卡在 2008 R2 上,我认为您想要做的是在存储过程中使用只读前向游标。然后在 WHILE @@FETCH_STATUS = 0 循环内部使用 TRY CATCH 块。

DECLARE logcursor CURSOR FORWARD_ONLY READ_ONLY FOR 
SELECT TOP 319 [LogId]
          ,[formattedmessage]
       FROM [GenesisLogging].[dbo].[Log] (NOLOCK)

OPEN logcursor

FETCH NEXT FROM logcursor
INTO @id, @formattedmessage

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
    SET @xml = CONVERT(xml, @formattedmessage)
    END TRY
    BEGIN CATCH
    PRINT @id
    END CATCH; 
    FETCH NEXT FROM logcursor 
    INTO @id, @formattedmessage
END 
CLOSE logcursor;
DEALLOCATE logcursor;
Run Code Online (Sandbox Code Playgroud)