5

我已通读 SO:XML 数据类型方法“值”必须是字符串文字,但我的问题有点不同。我在一个变量中有一些 xml,我想把它分开并给出一条路径。最初我试过这个:

declare @x xml
select @x = '....'
select @x.value('(' + @path + ')[1]', 'varchar(max)')

但是,当然,这失败了。然后我找到了 sql:variable 并尝试了这个:

select @x.value('(sql:variable("@path"))[1]', 'varchar(max)')

但这奇怪地返回了@path 的值(为什么?)。我一直在弄乱它,但无法让它做正确的事情。

有人想吗?

4

3 回答 3

3

您的选择返回值 of@path因为sql:variable()返回一个文字值,因此实际上您是在要求 SQL 服务器@path从文档中选择文字值,它会这样做。我知道做你想做的事情的唯一方法是使用动态 SQL,如下所示:

declare @xml xml = '
<root>
    <element attr="test">blah</element>
</root>';

declare @p nvarchar(max) = '(//element/text())[1]';
declare @sql nvarchar(max) 
    = 'select @x.value(''' + @p + ''', ''nvarchar(max)'')';

exec sp_executesql @sql, @parameters = N'@x xml', @x = @xml;

但我应该警告你,这不是很好的做法(想想 SQL 注入、验证输入等)

于 2012-09-13T11:12:59.873 回答
2

在 Microsoft 网站上wBob的帮助下,我现在有了一个干净的解决方案。当然,性能是一个问题,因为整个文档将被映射为单一路径,但改进留给读者作为建议的可能性:)

if object_id('VMConfigVal') is not null
drop function VMConfigVal
go
create function VMConfigVal(@x xml, @path varchar(max))
returns nvarchar(max)
as
begin
    declare @ret nvarchar(max)

    ;with cte as
    (
    select  value = x.c.value('.', 'varchar(50)')
    ,       path = cast ( null as varchar(max) )
    ,       node = x.c.query('.')
    from    @x.nodes('/*') x(c)
    union all
    select  n.c.value('.', 'varchar(50)')
    ,       isnull( c.path + '/', '/' )
        +       n.c.value('local-name(.)', 'varchar(max)')
    ,       n.c.query('*')
    from    cte c
    cross   apply c.node.nodes('*') n(c)
    )
    select @ret = value from cte where path = @path
    return @ret
    end
go

所以我现在可以做类似的事情:

select dbo.VMConfigVal(MyXMLConfig, '/hardware/devices/IDE/ChannelCount')
from someTable

甜的!

于 2012-09-19T23:21:59.957 回答
1

如果您只需要按名称查找子元素并想从 XPath 文字中抽象出名称,这里有一些选项:

// Returns the /root/node/element/@Value with @Name contained in @AttributeName SQL variable.
SELECT @Xml.value('(/root/node/element[@Name=sql:variable("@AttributeName")]/@Value)[1]', 'varchar(100)')

// Returns the text of the child element of /root/node with the name contained in @ElementName SQL variable.
SELECT @Xml.value('(/root/node/*[name(.)=sql:variable("@ElementName")]/text())[1]', 'varchar(100)')

// Searching the xml hierarchy for elements with the name contained in @ElementName and returning the text().
SELECT @Xml.value('(//*[name(.)=sql:variable("@ElementName")]/text())[1]', 'varchar(100)')

您需要声明@ElementName 或@AttributeName SQL 变量来运行这些。我测试了第一个语句,但没有明确测试其他 2 个语句,仅供参考。

于 2013-02-12T22:23:03.153 回答