我有一个nvarchar(max)
列的表,可能包含已知结构的xml。我想解析它来执行一些聚合,所以需要过滤掉“坏”条目。以下是测试用例:
create table TestTable ([Message] nvarchar(max))
insert into TestTable ([Message]) values
('<root m="1"/>'),
('<root m="7"/>'),
('<rooo')
go
set quoted_identifier on
go
create view TestView as
select data.value('(/root/@m)[1]', 'int') as MyValue
from (
select cast([Message] as xml) as data
from (
select [Message] from dbo.TestTable where [Message] like '<root%>'
) as T1
) as T2
where data.exist('/root') = 1
go
select * from TestView
这会产生:
Msg 9400,Level 16,State 1,Line 1 XML解析:第1行,第5个字符, 意外的输入结束
我不明白为什么,因为如果我运行嵌套查询:
select cast([Message] as xml) as data
from (
select [Message] from dbo.TestTable where [Message] like '<root%>'
) as T1
它完美地返回2个有效行。为什么?
P.S。 Microsoft SQL Server 2008(SP3) - 10.0.5500.0(X64)2011年9月21日22:45:45版权所有(c)1988-2008 Windows NT 6.1上的Microsoft Corporation Express Edition(64位)(Build 7601:Service Pack 1)
答案 0 :(得分:1)
您的问题不在select子句中,而是在where子句中:
where data.exist('/root') = 1
我怀疑你在想:“啊哈!如果xml没有正确格式化,那么这将返回0或NULL”。不,这个函数 - 和其他xml函数一样 - 需要有效的xml。或者它出错了。
您可能对SQL Server: inline conditional convert with XML?和In SQL Server, what is the best way to determine if a given string is a valid XML or not?感兴趣。
似乎没有一种简单的方法可以做你想要的。但是,您可以对原始字符串进行简单检查,看看它是否合理。例如,以下检查是否有相同数量的“&lt;”和“&gt;”:
select (case when len(replace(val, '<', '')) = len(replace(val, '>', ''))
then 'MAYBE OKAY'
else 'NOPE'
end)
from (select '<badness' as val) t