当前位置: 首页 > 图文教程 > 数据库 > MSSQL > SQLServer2005 XML在T-SQL中的应用

MSSQL
sql 批量修改数据库表
mssql CASE,GROUP BY用法
利用SQL SERVER建立登录WINDOWS帐号
SQL Server 2008 正式版安装指南 包含序列号
SQL Server 2008图文安装教程
sql 语句 取数据库服务器上所有数据库的名字
sqlserver 数据类型转换小实验
SQL Server 存储过程解析
压缩技术给SQL Server备份文件瘦身
SQL Server 2005 还原数据库错误解决方法
Sql Server datetime问题
SQL语句 操作全集 学习mssql的朋友一定要看
格式导致的Excel导入sql出现异常的解决方法
SQL Server 数据库自动执行管理任务
sql Set IDENTITY_INSERT的用法
sql 修改表的所有者
过程需要参数 ''@statement'' 为 ''ntext/nchar/nvarchar'' 类型
mssql 建立索引
SQL Server 索引结构及其使用(一)--深入浅出理解索引结构
SQL Server 索引结构及其使用(二) 改善SQL语句

MSSQL 中的 SQLServer2005 XML在T-SQL中的应用


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-17   浏览: 50 ::
收藏到网摘: n/a

以下为引用的内容:

*
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