从SQL Server 2008中的XML字段中选择值


111

仅查看我的XML字段,我的行将如下所示:

<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>

请注意,这是我表中的三行。

我想以表格形式返回SQL结果

Jon  | Johnson
Kathy| Carter
Bob  | Burns

什么查询可以完成此任务?


有没有办法只获取xml中的所有元素?您必须一一指定吗?这真的非常乏味。您可以执行“从表中选择*”,似乎应该可以执行“从xml中选择xml。*”,而不必指定所需的每个元素。
基思·泰勒

Answers:


156

假设XML字段名为“ xmlField” ...

SELECT 
[xmlField].value('(/person//firstName/node())[1]', 'nvarchar(max)') as FirstName,
[xmlField].value('(/person//lastName/node())[1]', 'nvarchar(max)') as LastName
FROM [myTable]

16
如果xmlField包含多个<person>元素,则必须使用.nodes()并交叉应用。
雷木斯·鲁萨努

SQL Server 2008 R2的快递,回到我这个错误与您的解决方案:The XQuery syntax '/function()' is not supported.; 另一方面,@ Remus Rusanu似乎可以做到:)
RMiranda

2
奇怪 它被投票了102次,但此答案仅返回第一个 XML记录中的数据。它指的是[myTable]表...那是从哪里来的?
Mike Gledhill

我已经尝试了很多次,但从未奏效。我的XML是<BAM><Type>Electrical</Type><BaIds><a:int>7330</a:int></BaIds></BAM>,我的选择是select e.MessageData.value('(/BAM/Type)[1]', 'varchar(100)')。我也曾尝试选择e.MessageData.value('(/BAM/Type/node())[1]', 'varchar(100)'),并且'(//Type/node())[1]''(./Type)[1]'和所有其他组合,我能想到的。我所得到的只是NULL
JonathanPeel

1
@MikeGledhill它从多个XML记录返回值对我来说很好。OP给该表的唯一名称也是“我的表” :)
Paul

122

考虑到XML数据来自表'table'并存储在'field'列中:使用XML方法,使用提取值,使用提取xml.value()项目节点xml.nodes(),使用CROSS APPLY联接:

SELECT 
    p.value('(./firstName)[1]', 'VARCHAR(8000)') AS firstName,
    p.value('(./lastName)[1]', 'VARCHAR(8000)') AS lastName
FROM table 
    CROSS APPLY field.nodes('/person') t(p)

如果每个字段仅包含一个元素“人” nodes()cross apply则可以放弃。如果XML是变量,则选择FROM @variable.nodes(...),则不需要cross apply


1
我想知道这种方法的效率如何,还有没有更好的方法。与XPath结果的CROSS APPLY组合似乎会导致相当多的资源需求查询。
redcalx10年

1
@thelocster:这与普通的数据访问没有什么不同。改善XML性能的技术已被详细记录。msdn.microsoft.com/en-us/library/ms345118%28SQL.90%29.aspx
Remus Rusanu 2010年

2
请记住,如果您的XML定义了xmlns命名空间,则需要在上面的XQuery(XPath)表达式中定义它们。有关示例,请参见stackoverflow.com/a/1302150/656010
汤姆·韦森

与我所需要的稍有不同,但这是解决我所遇到的一个问题的完美解决方案,该问题是带有XML列的多行-我想遍历行并从XML列中提取数据字段并将其放入插入语句。因此,5行,每个XML字段中的3列数据= 15次插入,完美。
dan richardson 2012年

17

这篇文章对解决我的XML格式略有不同的问题很有帮助...我的XML包含以下示例所示的键列表,并且我将XML存储在名为DeleteBatch的表的SourceKeys列中:

<k>1</k>
<k>2</k>
<k>3</k>

创建表并用一些数据填充它:

CREATE TABLE dbo.DeleteBatch (
    ExecutionKey INT PRIMARY KEY,
    SourceKeys XML)

INSERT INTO dbo.DeleteBatch ( ExecutionKey, SourceKeys )
SELECT 1, 
    (CAST('<k>1</k><k>2</k><k>3</k>' AS XML))

INSERT INTO dbo.DeleteBatch ( ExecutionKey, SourceKeys )
SELECT 2, 
    (CAST('<k>100</k><k>101</k>' AS XML))

这是从XML中选择键的SQL:

SELECT ExecutionKey, p.value('.', 'int') AS [Key]
FROM dbo.DeleteBatch
    CROSS APPLY SourceKeys.nodes('/k') t(p)

这是查询结果...

ExecutionKey键
1 1
1 2
1 3
2 100
2 101

9

这可能会回答您的问题:

select cast(xmlField as xml) xmlField into tmp from (
select '<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>' xmlField
union select '<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>'
union select '<person><firstName>Bob</firstName><lastName>Burns</lastName></person>'
) tb

SELECT
    xmlField.value('(person/firstName)[1]', 'nvarchar(max)') as FirstName
    ,xmlField.value('(person/lastName)[1]', 'nvarchar(max)') as LastName
FROM tmp

drop table tmp

6

布里米 这是一个非常有用的发现线程。

我仍然发现其中一些建议令人困惑。每当我在字符串中使用valuewith [1]时,它只会检索第一个值。并建议使用一些建议cross apply(在我的测试中),这些建议会带回太多数据。

因此,这是我的一个简单示例,说明如何创建xml对象,然后将其值读出到表中。

DECLARE @str nvarchar(2000)

SET @str = ''
SET @str = @str + '<users>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Mike</firstName>'
SET @str = @str + '     <lastName>Gledhill</lastName>'
SET @str = @str + '     <age>31</age>'
SET @str = @str + '  </user>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Mark</firstName>'
SET @str = @str + '     <lastName>Stevens</lastName>'
SET @str = @str + '     <age>42</age>'
SET @str = @str + '  </user>'
SET @str = @str + '  <user>'
SET @str = @str + '     <firstName>Sarah</firstName>'
SET @str = @str + '     <lastName>Brown</lastName>'
SET @str = @str + '     <age>23</age>'
SET @str = @str + '  </user>'
SET @str = @str + '</users>'

DECLARE @xml xml
SELECT @xml = CAST(CAST(@str AS VARBINARY(MAX)) AS XML) 

--  Iterate through each of the "users\user" records in our XML
SELECT 
    x.Rec.query('./firstName').value('.', 'nvarchar(2000)') AS 'FirstName',
    x.Rec.query('./lastName').value('.', 'nvarchar(2000)') AS 'LastName',
    x.Rec.query('./age').value('.', 'int') AS 'Age'
FROM @xml.nodes('/users/user') as x(Rec)

这是输出:

在此处输入图片说明

这是一种奇怪的语法,但是有一个不错的示例,将其添加到您自己的SQL Server函数中很容易。

说到这里,这是对这个问题的正确答案。

假设您的xml数据@xml的类型xml为变量(如上面的示例所示),这是从问题中引用的xml返回三行数据的方式:

SELECT 
    x.Rec.query('./firstName').value('.', 'nvarchar(2000)') AS 'FirstName',
    x.Rec.query('./lastName').value('.', 'nvarchar(2000)') AS 'LastName'
FROM @xml.nodes('/person') as x(Rec)

在此处输入图片说明


我看不出这是正确的答案。OP要求从XML类型的表中查询一列,在这种情况下,您必须使用[1],索引序号来强制它返回1行,或者必须交叉应用列nodes()以获取可以对它运行xpath的结构。如果不进行大量修改,您的代码不会转换为该方案。您使用的是变量,而不是表列。您还过度使用了query()返回xml的函数。例如,您可能只有x.Rec.value('(./firstName)[1]', 'nvarchar(2000)') AS FirstName
Davos

3

如果您能够将XML包装在根元素中-请说以下是您的解决方案:

DECLARE @PersonsXml XML = '<persons><person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person></persons>'

SELECT  b.value('(./firstName/text())[1]','nvarchar(max)') as FirstName, b.value('(./lastName/text())[1]','nvarchar(max)') as LastName
FROM @PersonsXml.nodes('/persons/person') AS a(b)

在此处输入图片说明


3

MSSQL使用常规XPath规则,如下所示:

  • nodename选择名称为“ nodename”的所有节点
  • /从根节点选择
  • //从当前节点中选择匹配选择的节点,无论它们在何处
  • 。选择当前节点
  • ..选择当前节点的父节点
  • @选择属性

W3学校


2
SELECT 
cast(xmlField as xml).value('(/person//firstName/node())[1]', 'nvarchar(max)') as FirstName,
cast(xmlField as xml).value('(/person//lastName/node())[1]', 'nvarchar(max)') as LastName
FROM [myTable]

0

/ *本示例使用带有模式的XML变量* /

IF EXISTS (SELECT * FROM sys.xml_schema_collections 
           WHERE name = 'OrderingAfternoonTea')
BEGIN
    DROP XML SCHEMA COLLECTION dbo.OrderingAfternoonTea 
END
GO

CREATE XML SCHEMA COLLECTION dbo.OrderingAfternoonTea AS
N'<?xml version="1.0" encoding="UTF-16" ?>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     targetNamespace="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     xmlns="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     xmlns:TFor2="http://Tfor2.com/schemas/actions/orderAfternoonTea"
     elementFormDefault="qualified"
     version="0.10"
   > 
    <xsd:complexType name="AfternoonTeaOrderType">
       <xsd:sequence>
         <xsd:element name="potsOfTea" type="xsd:int"/>
         <xsd:element name="cakes" type="xsd:int"/>
         <xsd:element name="fruitedSconesWithCream" type="xsd:int"/>
         <xsd:element name="jams" type="xsd:string"/>
      </xsd:sequence>
      <xsd:attribute name="schemaVersion" type="xsd:long" use="required"/>
    </xsd:complexType>

    <xsd:element name="afternoonTeaOrder"
                 type="TFor2:AfternoonTeaOrderType"/>

  </xsd:schema>' ;
GO

DECLARE @potsOfTea int;
DECLARE @cakes int;
DECLARE @fruitedSconesWithCream int;
DECLARE @jams nvarchar(128);

DECLARE @RequestMsg NVARCHAR(2048);
DECLARE @RequestXml XML(dbo.OrderingAfternoonTea);

set @potsOfTea = 5;
set @cakes = 7;
set @fruitedSconesWithCream = 25;
set @jams = N'medlar jelly, quince and mulberry';

SELECT @RequestMsg = N'<?xml version="1.0" encoding="utf-16" ?>
<TFor2:afternoonTeaOrder schemaVersion="10"
    xmlns:TFor2="http://Tfor2.com/schemas/actions/orderAfternoonTea">
    <TFor2:potsOfTea>' + CAST(@potsOfTea as NVARCHAR(20)) 
        + '</TFor2:potsOfTea>
    <TFor2:cakes>' + CAST(@cakes as NVARCHAR(20)) + '</TFor2:cakes>
    <TFor2:fruitedSconesWithCream>' 
        + CAST(@fruitedSconesWithCream as NVARCHAR(20))
        + '</TFor2:fruitedSconesWithCream>
    <TFor2:jams>' + @jams + '</TFor2:jams>
</TFor2:afternoonTeaOrder>';

SELECT @RequestXml  = CAST(CAST(@RequestMsg AS VARBINARY(MAX)) AS XML) ;

with xmlnamespaces('http://Tfor2.com/schemas/actions/orderAfternoonTea'
                    as tea)
select
    cast( x.Rec.value('.[1]/@schemaVersion','nvarchar(20)') as bigint )
        as schemaVersion,
    cast( x.Rec.query('./tea:potsOfTea')
               .value('.','nvarchar(20)') as bigint ) as potsOfTea,
    cast( x.Rec.query('./tea:cakes')
               .value('.','nvarchar(20)') as bigint )  as cakes,
    cast( x.Rec.query('./tea:fruitedSconesWithCream')
               .value('.','nvarchar(20)') as bigint ) 
      as fruitedSconesWithCream,
    x.Rec.query('./tea:jams').value('.','nvarchar(50)')  as jams
from @RequestXml.nodes('/tea:afternoonTeaOrder')  as x(Rec);

select @RequestXml.query('/*')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.