/*
SQLServer2005 XML在T-SQL查询中的典型应用
整理:fcuandy
时间:2008.11.7
前言:
此文只讲xml数据类型及相应的一些操作方法在解决日常T-SQL编程中的一些应用,而避开xml modify,
xml schema,xml索引,命名空间等这些语法性或者生硬的一些问题(这些语法您可以查联机丛书),即此文主要
讲以xml的一些操作特性及xquery去解决编程问题.
Tags:
xquery ,FLWOR迭带 ,sql:column ,sql:variable ,nodes ,value ,query ,xpath ,xquery function, if, 聚合函数, xs:function等
典型应用举例:
*/
--(1)
--====================================================================
--拆分
DECLARE @s VARCHAR(100)
SET @s='a,b,c,dd,ee,f,aa,a,aa,f'
--常规做法(sql2000常用),以一split函数拆分串为表类型结构,如
--SELECT * FROM dbo.split(@s,',') a
--当然,也可能是循环去拆分,或者以一输助表的identity列利用charindex等函数拿identity列值与','的位置匹配实现拆分
--这些做法,roy_88及本人以前都整理过,不再累赘,可见推荐贴。即便 是xml法,也贴过多次,下面一笔带过
--XML做法:
SELECT b.v FROM
(SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a --将字串","换换为"</r><r>"并前后拼上<r>,</r>以用来构造xml串
CROSS APPLY
(SELECT v=t.x.value('.','VARCHAR(10)') FROM a.x.nodes('//r') AS t(x) ) b --使用 xml.nodes函数将xml串拆分为行
/*
a
b
c
dd
ee
f
aa
a
aa
f
*/
--(2)
--====================================================================
--去重,@s中出现的元素,重复的只要一个,希望结果为 'a,b,c,dd,ee,f'
--常规做法,循环或函数,或临时表拆后distinct
--XML做法:
--a.在(1)的基础上进行
;WITH fc AS --定义cte命名,将@s转换为一个表结构
(
SELECT DISTINCT b.v v
FROM
(SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
CROSS APPLY
(SELECT v=t.x.value('.','VARCHAR(10)') FROM a.x.nodes('//r') AS t(x) ) b
)
--对这个表利用xml方法进行行值拼接
SELECT STUFF(b.v.value('/r[1]','varchar(100)'),1,1,'')
FROM
(SELECT v=(SELECT ',' + v FROM fc FOR XML PATH(''),ROOT('r'),TYPE)) b
/*
a,aa,b,c,dd,ee,f
*/
--b FLWOR语句 + T-SQL组合:
SELECT STUFF(v,1,1,'') FROM
(SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
CROSS APPLY
(SELECT x=(SELECT t.x.value('.','varchar(10)') v,idx=ROW_NUMBER() OVER(ORDER BY GETDATE()) FROM a.x.nodes('//r') AS t(x) FOR XML PATH('r'),TYPE)) b --利用row_number得到唯一idx
CROSS APPLY
(SELECT v=CAST(b.x.query('for $r in //r where count(//r[v=$r/v and idx<$r/idx])=0 return concat(",",xs:string($r/v[1]))') AS VARCHAR(MAX))) c --类似count计数法,取得v相同的节点集idx值最小的节点,原型为:
--SELECT * FROM tb a WHERE 1>(SELECT COUNT(*) FROM tb WHERE v=a.v AND id<a.id)
/*
a ,b ,c ,dd ,ee ,aa ,f
*/
--c distinct-values
SELECT REPLACE(v,' ',',') FROM
(SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
CROSS APPLY
(SELECT CAST(a.x.query('distinct-values(//r)') AS VARCHAR(MAX)) v) b --直接调用distinct-values函数来操作
/*
a,b,c,dd,ee,f,aa
*/
-- 导入去重, last() , position()
DECLARE @doc xml
SET @doc ='<?xml version="1.0" encoding="gb2312" ?>
<employees>
<employee>
<empid>e0001</empid>
<name>萧峰</name>
</employee>
<employee>
<empid>e0002</empid>
<name>段誉</name>
</employee>
<employee>
<empid>e0003</empid>
<name>王语嫣</name>
</employee>
<employee>
<empid>e0003</empid>
<name>张无忌</name>
</employee>
</employees>
'
create table people2
(
personid varchar(10) primary key ,
name varchar(20)
)
INSERT people2
SELECT DISTINCT b.* FROM
(SELECT x = @doc.query('for $e in //employee return //employee[empid = $e/empid][last()]')) a --FLWOR时,用当前节点去//emploee节点集中找节点集中empid等于当前节点的empid, 在找到的集合中取最后一个利用last()函数
CROSS APPLY
(SELECT id=t.x.value('empid[1]','varchar(100)'),name=t.x.value('name[1]','varchar(100)') FROM a.x.nodes('//employee') AS t(x)) b
SELECT * FROM people2
/*
e0001 萧峰
e0002 段誉
e0003 张无忌
*/
GO
drop table people2
GO
--同组一选多,也可应用此方法,不过没有必要,就不再累赘了。
--(3)
--====================================================================
--列名,列值相关
--a,按行聚合
declare @t table(Sname nvarchar(5), V1 float, V2 float, V3 float, V4 float, V5 float, V6 float)
insert @t select N'张三', 0.11 , 0.21 , 0.29, 0.32 , 0.11, 0.08
insert @t select N'李四', 0.01 , 0.61 , 0.21, 0.73 , 0.21, 0.12
insert @t select N'张五', 0.31 , 0.21 , 0.23, 0.33 , 0.91, 0.65
insert @t select N'张六', 0.59 , 0.11, 0.26, 0.13, 0.01, 0.15
select b.* from
(select x=cast((select * from @t for xml path('r')) as xml)) a
cross apply
(
select name=x.query('./Sname/text()'),v=x.query('max(./*[local-name(.)!="Sname"])') from a.x.nodes('//r') as t(x)
--r为二级节点(因为文档本身无根节点,即为每项的顶级节点)即为一个r节点表示一条记录. r下级节点,每个表示一个列,因为列名未知,所以用/*匹配所有节点,因为name为区别列,不参与聚合运算,故用local-name取得来过滤
) b
/*
张三 0.32
李四 0.73
张五 0.91
张六 0.59
*/
--b ,由值引到取列
if not object_id('T1') is null
drop table T1
GO
Create table T1([tId] int,[tName] nvarchar(4))
Insert T1
select 1,N'zhao' union all
select 2,N'qian' union all
select 3,N'sun'
Go
--> --> 借且(Roy)生成測試數據
if not object_id('T2') is null
drop table T2
Go
Create table T2([tId] int,[zhao] nvarchar(1),[qian] nvarchar(1),[sun] nvarchar(1))
Insert T2
select 1,N'a',N'b',N'c' union all
select 2,N'd',N'e',N'f' union all
select 3,N'g',N'h',N'i'
Go
SELECT c.tid,c.tName,v FROM t1 c
CROSS APPLY
(SELECT x=(SELECT * FROM t2 WHERE tid=c.tid FOR XML PATH('r'),TYPE)) a
CROSS APPLY
(SELECT v=t.x.query('./*[local-name(.)=xs:string(sql:column("c.tName")) ]/text()')
FROM a.x.nodes('//r') AS t(x)
) b
/*
1 zhao a
2 qian e
3 sun i
*/
--c, 列名,列值,与系统表
CREATE TABLE tb(f1 INT,f2 INT,x INT,z INT,d INT,ex INT,dd INT,vv INT)
INSERT tb SELECT 1,2,3,5,11,3,2423,33
GO
SELECT * FROM tb
GO
SELECT name,v FROM
( SELECT name FROM sys.columns WHERE object_id=object_id('tb','u') ) a
CROSS JOIN
(SELECT x=(SELECT * FROM tb FOR XML PATH('r'),TYPE)) b
CROSS APPLY
(SELECT v=t.x.query('./*[local-name(.)=xs:string(sql:column("a.name")) ]/text()') FROM b.x.nodes('//r') AS t(x) ) c
/*
f1 1
f2 2
x 3
z 5
d 11
ex 3
dd 2423
vv 33
*/
GO
DROP TABLE tb
GO
--(4)
--一些综合计算
--以下表 ta.a值 yyyymmdd-yyyymmdd表连续时间段,","表单个日期
If object_id('ta','u') is not null
Drop table ta
Go
Create table ta(a varchar(100))
Go
Insert into ta
select '1 | |20080101-20080911'
union all
select '2 | |20080101,20080201,20080301,20080515,20080808'
union all
select '3 | |20080101,20080201,20080301,20080515,20081108'
Go
declare @s varchar(8)
select @s= convert(varchar(8),getdate(),112)
select stuff(replace(replace(cast(x as varchar(1000)),'</item><item>',case when type='1' then '-' else ',' end),'</item>',''),1,6,type + ' | |') a
from
(
select left(a,1) type,
cast(
'<item>'
+
replace(
stuff(a,1,5,''),
case when left(a,1)=1 then '-' else ',' end,
'</item><item>'
)
+
'</item>'
AS XML
) x
from ta
) base
where x.value('
if (sql:column("base.type")="1") then
if(
(/item/text())[1]<sql:variable("@s")
and
(/item/text())[2]>sql:variable("@s")
)
then 1
else 0
else
count(//item[text()>sql:variable("@s")])
'
,
'int'
)>0
go
SQL Server中用XQuery分解XML数据
本文讨论SQL Server 2005的新功能,它允许你将XML数据分解到关系格式中,而不必耗用太多内存。
在举例说明如何分解上一篇文章中的数据时,我们首先了解一下XQuery和它在SQL Server 2005中为开发者提供的功能。
XQuery介绍
XQuery,也称作XML Query,是一种查询XML数据的语言,允许你提取所需的节点和元素。它由W3C定义,可用于今天的大多数主流数据库引擎中,如Oracle、DB2和SQL Server。
SQL Server 2005 XQuery函数
下面的四个函数是SQL Server 2005中的XQuery函数。(注意,XML、XQuery语句和下面的函数都区分大小写。例如,SQL编译器接受XML数据中的.exist,但拒绝.EXIST或.Exist。)
xml.exist
这个方法根据一个XML节点上的搜索表达式返回一个布尔值。例如,列表A中XML代码片段中的语句将返回1(真):
SELECT @x.exist('/christmaslist/person[@gift = "socks"]')
这个语句返回0(假):
SELECT @x. exist ('/christmaslist/zach')
由于“Socks”一词被封套,这个语句将返回0(假)。
SELECT @x.exist('/christmaslist/person[@gift = "socks"]')
xml.value
这个方法接受一个XQuery语句并返回一个单独值。使用列表A中同样的XML代码片段,不可以使用VALUE函数生成“betty”值,如下所示:
SELECT @x.value('/christmaslist[1]/person[1]/@name', 'VARCHAR(20)')
而XQuery生成“zach”值。
SELECT @x.value('/christmaslist[1]/person[2]/@name', 'VARCHAR(20)')
xml.query
这个方法接受一个XQuery并返回一个XML数据类型的实例。可以按需要将这些查询简单或复杂化,下面是一个简单的例子:
SELECT @x.query('/christmaslist/person')
它返回XML文件:
<person name="betty" gift="camera" />
<person name="zach" gift="elmo doll" />
<person name="brad" gift="socks" />
xml.nodes
在你需要将一个XML数据类型变量中的数据分解到关系数据中时,这个方法十分有用。这个方法接受一个XQuery语句作为参数,并返回一个包含XML变量逻辑标量数据的行集。列表B中的查询利用上面定义的XML变量,并将数据分解到一个结果集中,它显示在XML变量中定义的人物姓名。
修改OPENXML存储过程
现在我来说明如何修改上周的OPENXML存储过程,使其可以应用XQuery功能。首先,我往XML变量中加载一些数据。如列表C所示。我们可以建立一个接受XML参数的过程,再应用XQuery函数把XML文件中的数据插入一个表中,而不必应用OPENXML。如列表D所示。
最初在数据库中应用XML似乎有些难于处理,还要花一些时间习惯使用XQuery和Xpath查询。但是,经过一段时间的学习以后,你就会发现在数据库中应用XML数据相当实用。
例如,在上述存储过程中应用XML数据,你只需调用一次数据库,而不必像典型存储过程编程那样调用N次数据库。这二者似乎区别不大,但对一个繁忙的系统而言,应用XML数据会有很大益处。而且,应用XQuery而非OPENXML还会显著提高性能,对小型XML文件更是如此。
对我有用[0]丢个板砖[0]引用举报管理TOP
精华推荐:[分享]查看索引信息/生成创建索引脚本
fredrickhu
(小F)
等 级:
2
5
更多勋章
#3楼 得分:0回复于:2010-02-02 09:51:09
SQL code
select @data.query('//address[@data="+534654765876876"]')
对我有用[0]丢个板砖[0]引用举报管理TOP
精华推荐:牢记!SQL Server数据库开发的二十一条军规(SQL收藏)
fredrickhu
(小F)
等 级:
2
5
更多勋章
#4楼 得分:0回复于:2010-02-02 09:51:30
SQL code
/*
sql xml 入门:
--by jinjazz
--http://blog.csdn.net/jinjazz
1、xml: 能认识元素、属性和值
2、xpath: 寻址语言,类似windows目录的查找(没用过dir命令的话就去面壁)
语法格式,这些语法可以组合为条件:
"."表示自己,".."表示父亲,"/"表示儿子,"//"表示后代,
"name"表示按名字查找,"@name"表示按属性查找
"集合[条件]" 表示根据条件取集合的子集,条件可以是
数 值:数字,last(),last()-数字 等
布尔值:position()<数字,@name='条件',name='条件'
条件是布尔值的时候可以合并计算:and or
3、xquery: 基于xpath标的准查询语言,sqlserver xquery包含如下函数
exist(xpath条件):返回布尔值表示节点是否存在
query(xpath条件):返回由符合条件的节点组成的新的xml文档
value(xpath条件,数据类型):返回指定的标量值,xpath条件结果必须唯一
nodes(xpath条件): 返回由符合条件的节点组成的一行一列的结果表
*/
declare @data xml
set @data='
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="jp">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="cn">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
'
--测试语句,如果不理解语法请参考上面的xpath规则和xquery函数说明
--1、文档
select @data
--2、任意级别是否存在price节点
select @data.exist('//price')
--3、获取所有book节点
select @data.query('//book')
--4、获取所有包含lang属性的节点
select @data.query('//*[@lang]')
--5、获取第一个book节点
select @data.query('//book[1]')
--6、获取前两个book节点
select @data.query('//book[position()<=2]')
--7、获取最后一个book节点
select @data.query('//book[last()]')
--8、获取price>35的所有book节点
select @data.query('//book[price>35]')
--9、获取category="WEB"的所有book节点
select @data.query('//book[@category="WEB"]')
--10、获取title的lang="en"的所有book节点
select @data.query('//book/title[@lang="en"]')
--11、获取title的lang="en"且 price>35的所有book节点
select @data.query('//book[./title[@lang="en"] or price>35 ]')
--12、获取title的lang="en"且 price>35的第一book的(第一个)title
select @data.query('//book[./title[@lang="en"] and price>35 ]').value('(book/title)[1]','varchar(max)')
--13、等价于12
select @data.value('(//book[./title[@lang="en"] and price>35 ]/title)[1]','varchar(max)')
--14、获取title的lang="en"且 price>35的第一book的(第一个)title的lang属性
select @data.value('((//book[@category="WEB" and price>35 ]/title)[1]/@lang)[1]','varchar(max)')
--15、获取第一本书的title
select Tab.Col.value('(book/title)[1]','varchar(max)') as title
from @data.nodes('bookstore')as Tab(Col)
--16、获取每本书的第一个author
select Tab.Col.value('author[1]','varchar(max)') as title
from @data.nodes('//book')as Tab(Col)
--17、获取所有book的所有信息
select
T.C.value('title[1]','varchar(max)') as title,
T.C.value('year[1]','int') as year,
T.C.value('title[1]','varchar(max)')as title,
T.C.value('price[1]','float') as price,
T.C.value('author[1]','varchar(max)') as author1,
T.C.value('author[2]','varchar(max)') as author2,
T.C.value('author[3]','varchar(max)') as author3,
T.C.value('author[4]','varchar(max)') as author4
from @data.nodes('//book') as T(C)
--18、获取不是日语(lang!="jp")且价格大于35的书的所有信息
select
T.C.value('title[1]','varchar(max)') as title,
T.C.value('year[1]','int') as year,
T.C.value('title[1]','varchar(max)')as title,
T.C.value('price[1]','float') as price,
T.C.value('author[1]','varchar(max)') as author1,
T.C.value('author[2]','varchar(max)') as author2,
T.C.value('author[3]','varchar(max)') as author3,
T.C.value('author[4]','varchar(max)') as author4
from @data.nodes('//book[./title[@lang!="jp"] and price>35 ]') as T(C)
http://www.tzwhx.com/newOperate/html/1/13/132/11692.html