Home  >  Article  >  Database  >  SqlServer 2005处理xml格式

SqlServer 2005处理xml格式

WBOY
WBOYOriginal
2016-06-07 15:27:30891browse

今天,在csdn上看到一则处理xml的问题,以前也有写过xml的查询,下面是问题 declare @x xml,@y xmlset @x='item id1 /id title姓名 /title value /value /item item id2 /id title年龄 /title value /value /item 'set @y='item id1 /id value张三 /value /i

    今天,在csdn上看到一则处理xml的问题,以前也有写过xml的查询,下面是问题 

declare @x xml,@y xml
set @x='<item> 
<id>1 </id> 
<title>姓名 </title> 
<value> </value> 
</item> 
<item> 
<id>2 </id> 
<title>年龄 </title> 
<value> </value> 
</item> 
'
set @y='<item> 
<id>1 </id> 
<value>张三 </value> 
</item> 
<item> 
<id>2 </id> 
<value>20 </value> 
</item> 
'有连个变量如上所示:
现在要求得到下面的格式:
/*
<item>
    <id>1</id>
    <title>姓名 </title>
    <value>张三 </value>
</item>
<item>
    <id>2</id>
    <title>年龄 </title>
    <value>20 </value>
</item>
*/

      下面是xml操作的答案:

select  
        D.x.value('./id[1]','int') AS id,
        D.x.value('./title[1]','nvarchar(100)') AS title,
        D.x.value('./value[1]','nvarchar(100)') AS [value]
    from @x.nodes('/*') as D(x)
    
;with t1
as(
    select  
        D.x.value('./id[1]','int') AS id,
        D.x.value('./title[1]','nvarchar(100)') AS title,
        D.x.value('./value[1]','nvarchar(100)') AS [value]
    from @x.nodes('/*') as D(x)),
t2
as(
    select  
        D.x.value('./id[1]','int') AS id,
        D.x.value('./value[1]','nvarchar(100)') AS [value]
    from @y.nodes('/*') as D(x))
select a.id,title,b.[value]
from t1 as a
left join t2 as b on a.id = b.id
for xml path('item')

    随着标准化的执行,我相信以后sql里会有更多对xml的操作。

    下面解读一下上面的sql:

WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。
特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

    然后就是两个对xml操作的函数:

如果希望将 XML 数据类型实例拆分为关系数据,nodes() 方法十分有用。它允许您标识将映射到新行的节点。

每一个 xml 数据类型实例都具有隐式提供的上下文节点。对于在列或变量中存储的 XML 实例来说,它是文档节点。文档节点是位于每个 xml 数据类型实例顶部的隐式节点。 nodes() 方法的结果是一个包含原始 XML 实例的逻辑副本的行集。在这些逻辑副本中,每个行示例的上下文节点都被设置成由查询表达式标识的节点之一。这样,后续的查询可以浏览与这些上下文节点相关的节点。 您可以从行集中检索多个值。例如,可以将 value() 方法应用于 nodes() 所返回的行集,从原始 XML 实例中检索多个值。请注意,当 value() 方法应用于 XML 实例时,它仅返回一个值。

     

示例
A. 对 xml 类型的变量使用 nodes() 方法
在此示例中,现有一个包含 <root> 顶级元素和三个 <row> 子元素的 XML 文档。此查询使用 nodes() 方法为每个 <row> 元素设置单独的上下文节点。nodes() 方法返回包含三行的行集。每行都有一个原始 XML 的逻辑副本,其中每个上下文节点都标识原始文档中的一个不同的 <row> 元素。

然后,查询会从每行返回上下文节点: 

 复制代码 
DECLARE @x xml 
SET @x='<root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3"></row>
</root>'
SELECT T.c.query('.') AS result
FROM   @x.nodes('/Root/row') T(c)
go
 

结果如下。在此示例中,查询方法返回上下文项及其内容:

 复制代码 
 <row id="1"><name>Larry</name><oflw>some text</oflw></row>
 <row id="2"><name>moe</name></row>
 <row id="3"></row>
 

对上下文节点应用父级取值函数将返回所有三行的 <root> 元素。

 复制代码 
SELECT T.c.query('..') AS result
FROM   @x.nodes('/Root/row') T(c)
go
 

结果如下:

 复制代码 
<root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3"></row>
</root>
<root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3"></row>
</root>
<root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3"></row>
</root>
 

下面的查询指定了绝对路径。对使用绝对路径表达式的上下文节点的查询,将从上下文节点的根节点开始进行。因此,您将收到由 nodes() 返回的每个上下文节点的全部三行。

 复制代码 
SELECT T.c.query('/Root/row') AS result
FROM   @x.nodes('/Root/row') T(c)
go
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3"></row>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3"></row>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3"></row>
 

请注意,由 xml 数据类型的 nodes() 方法返回的列无法直接使用。例如,下面的查询将返回错误:

 复制代码 
...
SELECT T.c
FROM   @x.nodes('/Root/row') T(c)
 

在以下查询中,xml 数据类型的 value() 和 query() 方法应用到由 nodes() 方法返回的行集中。value() 方法返回上下文项 (<row>) 的 id 属性;query() 方法返回上下文项的 <name> 元素子树。 

 复制代码 
DECLARE @x xml 
SET @x='
<root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>Joe</name></row>
    <row id="3"></row>
</root>
'
SELECT T.c.value('@id','int') as id,
       T.c.query('name') as NAME
FROM   @x.nodes('/Root/row') T(c)
go
 

结果如下:

 复制代码 
 id  NAME
-----------------------
 1   <name>Larry</name>
 2   <name>Joe</name>
 3   
 

请注意,结果包括行 ID 3 并且 <row> 元素不包含子 <name>。如果您希望对结果进行筛选,以便返回(或不返回)不带子 <name> 的行,就可以使用下列方法之一对其进行筛选: 

使用 nodes() 路径表达式(例如 /Root/row[name])中的谓词。


对行集使用 exist() 方法。


使用 CROSS APPLY。


使用 OUTER APPLY。


以下查询对 nodes() 返回的行集指定 exist() 方法。如果上下文节点 (<row>) 包含子 <name>,则 exist() 方法返回 True。

 复制代码 
DECLARE @x xml        
SET @x='<root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3"></row>       
</root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
WHERE T1.rows.exist('name') = 1;       
GO
 

此查询将返回两行:行 ID 分别为 1 和 2。 

以下查询使用 OUTER APPLY。OUTER APPLY 将 nodes() 应用于 T1(行)中的每个行,并返回构成结果集的行,也会返回 NULL。因此,WHERE 子句用于筛选行并只检索 T2.names 列不为 NULL 的行。

 复制代码 
DECLARE @x xml        
SET @x='       
<root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3"></row>       
</root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
OUTER APPLY T1.rows.nodes('./name') as T2(names)       
WHERE T2.names is not null       
GO       
 

以下查询使用 CROSS APPLY。CROSS APPLY 将 nodes() 应用于外部表 [T1(行)] 中的每一行,并且只返回当 nodes() 应用于 T1.rows 时构成结果集的行。在这种情况下,您不需要 WHERE 子句来测试 IS NOT NULL。

 复制代码 
DECLARE @x xml        
SET @x='<root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3"></row>       
</root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
CROSS APPLY T1.rows.nodes('./name') as T2(names)       
GO       
 

有关 CROSS APPLY 和 OUTER APPLY 的信息,请参阅使用 APPLY。

B. 针对 xml 类型的列指定 nodes() 方法
在此示例中使用自行车生产说明,并将其存储在 ProductModel 表的 Instructions xml 类型列中。有关详细信息,请参阅 AdventureWorks 数据库中的 xml 数据类型表示形式。 

在以下示例中,nodes() 方法是针对 ProductModel 表中 xml 类型的 Instructions 列指定的。 

nodes() 方法通过指定 /MI:root/MI:Location 路径将 <location> 元素设置为上下文节点。结果行集包括原始文档的逻辑副本,每个副本对应文档中的一个 <location> 节点,上下文节点设置为 <location> 元素。因此,nodes() 函数给出一组 <location> 上下文节点。 

query() 方法针对此行集请求 self::node,因此将返回每行中的 <location> 元素。 

在此示例中,查询在特定产品样式的生产说明文档中将每一个 <location> 元素都设置为上下文节点。您可以使用这些上下文节点来按照以下方式来检索值: 

在每个 <location> 中查找 LocationID 


在每个 <location> 中检索生产步骤(<step> 子元素) 


此查询使用 query() 方法返回上下文项,其中指定了 self::node() 的缩写语法 "."。

请注意以下方面: 

nodes() 方法应用于 Instructions 列并返回一个行集 T (C)。此行集包含将 /root/Location 作为上下文项的原始生产说明文档的逻辑副本。


CROSS APPLY 将 nodes() 应用于 Instructions 表中的每一行,并只返回构成结果集的行。 

 复制代码 
SELECT C.query('.') as result
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
declare namespace MI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
/MI:root/MI:Location') as T(C)
WHERE ProductModelID=7
 
下面是部分结果: 

 复制代码 
<location locationid="10" ...>
   <step ...></step>
      ...
</location>
<location locationid="20" ...>
    <step ...></step>
      ...
</location>
...
 

以下查询与先前的查询相似,但是它通过使用行集中的上下文节点,利用 value() 和 query() 检索一组值。对于每个位置,SELECT 子句都检索在该位置使用的位置 ID 和工具。 

 复制代码 
SELECT C.value('@LocationID','int') as LId,
       C.query('declare namespace MI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
                 MI:step/MI:tool') as result
FROM    Production.ProductModel
CROSS APPLY Instructions.nodes('
declare namespace MI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
/MI:root/MI:Location') as T(C)
WHERE ProductModelID=7
 

结果如下。为提高可读性,未显示命名空间。

 复制代码 
 LId  result
 10  <tool xmlns:mi="...">T-85A framing tool</tool>
     <tool xmlns:mi="...">Trim Jig TJ-26</tool>
     <tool xmlns:mi="...">router with a carbide tip 15</tool>
      <tool xmlns:mi="...">Forming Tool FT-15</tool>
 20
 30  <tool xmlns:mi="...">standard debur tool</tool>
 45  <tool xmlns:mi="...">paint harness</tool>
 50
 60
 

C. 将 nodes() 应用于由其他 nodes() 方法返回的行集
以下代码查询 ProductModel 表的 Instructions 列中生产说明的 XML 文档。此查询返回包含产品样式 ID、生产位置和生产步骤的行集。 

请注意以下方面: 

首先,nodes() 方法应用于 Instructions 列并返回 T1(位置)行集。此行集包含将 /root/Location 作为上下文项的原始生产说明文档的逻辑副本。


其次,nodes() 应用于 T1(位置)行集并返回 T2(步骤)行集。此行集包含将 /root/Location 作为上下文项的原始生产说明文档的逻辑副本。


 复制代码 
SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step       
FROM Production.ProductModel       
CROSS APPLY Instructions.nodes('       
declare namespace MI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";       
/MI:root/MI:Location') as T1(Locations)       
CROSS APPLY T1.Locations.nodes('       
declare namespace MI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";       
./MI:step ') as T2(steps)       
WHERE ProductModelID=7       
go       
-- result       
ProductModelID LocID Step       
----------------------------       
7      10   <step ...></step>       
7      10   <step ...></step>       
...       
7      20   <step ...></step>       
7      20   <step ...></step>       
7      20   <step ...></step>       
...       
 

此查询两次声明 MI 前缀。此外,您可以使用 WITH XMLNAMESPACES 来声明一次前缀并在查询中使用它:

 复制代码 
WITH XMLNAMESPACES (
   'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions'  AS MI)

SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step       
FROM Production.ProductModel       
CROSS APPLY Instructions.nodes('       
/MI:root/MI:Location') as T1(Locations)       
CROSS APPLY T1.Locations.nodes('       
./MI:step ') as T2(steps)       
WHERE ProductModelID=7       
go  
 

以下查询与先前的查询相似,但是它将 exist() 方法应用于 T2(步骤)行集中的 XML,以便只检索至少使用一个生产工具的生产步骤。即:<step> 元素至少包含一个 <tool> 子元素。

 复制代码 
WITH XMLNAMESPACES (
   'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS MI)

SELECT ProductModelID, 
       Locations.value('./@LocationID','int') as LocID,
       steps.query('.') as Steps
FROM   Production.ProductModel
CROSS APPLY Instructions.nodes('/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('./MI:step') as T2(steps)
WHERE  ProductModelID=7
AND    steps.exist('./MI:tool') = 1
Go

 

</tool></step></step></location></location></location></location></location></location></location></location></name></row></name></name></row></name></row></root></row></row></row></root>

      以上是一点小小的分享,希望对大家的sql进步有帮助。 

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn