SQLServer2005 XML在T-SQL查询中的典型应用

  1. /*
  2. SQLServer2005 XML在T-SQL查询中的典型应用
  3. 整理:fcuandy
  4. 时间:2008.11.7
  5. 前言:
  6.     此文只讲xml数据类型及相应的一些操作方法在解决日常T-SQL编程中的一些应用,而避开xml modify,
  7. xml schema,xml索引,命名空间等这些语法性或者生硬的一些问题(这些语法您可以查联机丛书),即此文主要
  8. 讲以xml的一些操作特性及xquery去解决编程问题.
  9. Tags:
  10.     xquery ,FLWOR迭带 ,sql:column ,sql:variable ,nodes ,value ,query ,xpath ,xquery function, if, 聚合函数, xs:function等
  11. 典型应用举例:
  12. */
  13. --(1)
  14. --====================================================================
  15. --拆分
  16. DECLARE @s VARCHAR(100)
  17. SET @s='a,b,c,dd,ee,f,aa,a,aa,f'
  18. --常规做法(sql2000常用),以一split函数拆分串为表类型结构,如
  19. --SELECT * FROM dbo.split(@s,',') a
  20. --当然,也可能是循环去拆分,或者以一输助表的identity列利用charindex等函数拿identity列值与','的位置匹配实现拆分
  21. --这些做法,roy_88及本人以前都整理过,不再累赘,可见推荐贴。即便 是xml法,也贴过多次,下面一笔带过
  22. --XML做法:
  23. SELECT b.v FROM
  24.     (SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a   --将字串","换换为"</r><r>"并前后拼上<r>,</r>以用来构造xml串
  25. CROSS APPLY
  26.     (SELECT v=t.x.value('.','VARCHAR(10)') FROM a.x.nodes('//r') AS t(x) ) b  --使用 xml.nodes函数将xml串拆分为行
  27. /*
  28. a
  29. b
  30. c
  31. dd
  32. ee
  33. f
  34. aa
  35. a
  36. aa
  37. f
  38. */
  39. --(2)
  40. --====================================================================
  41. --去重,@s中出现的元素,重复的只要一个,希望结果为 'a,b,c,dd,ee,f'
  42. --常规做法,循环或函数,或临时表拆后distinct
  43. --XML做法:
  44. --a.在(1)的基础上进行
  45. ;WITH fc AS   --定义cte命名,将@s转换为一个表结构
  46. (
  47.     SELECT DISTINCT b.v v
  48.             FROM
  49.                 (SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
  50.             CROSS APPLY
  51.                 (SELECT v=t.x.value('.','VARCHAR(10)') FROM a.x.nodes('//r') AS t(x) ) b
  52. )
  53. --对这个表利用xml方法进行行值拼接
  54. SELECT STUFF(b.v.value('/r[1]','varchar(100)'),1,1,'')
  55.     FROM
  56.     (SELECT v=(SELECT ',' + v FROM fc FOR XML PATH(''),ROOT('r'),TYPE)) b
  57. /*
  58. a,aa,b,c,dd,ee,f
  59. */
  60. --b FLWOR语句 + T-SQL组合:
  61. SELECT STUFF(v,1,1,'') FROM
  62.     (SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
  63. CROSS APPLY
  64.     (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
  65. CROSS APPLY
  66.     (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值最小的节点,原型为:
  67. --SELECT * FROM tb a WHERE 1>(SELECT COUNT(*) FROM tb WHERE v=a.v AND id<a.id)
  68. /*
  69. a ,b ,c ,dd ,ee ,aa ,f
  70. */
  71. --c distinct-values
  72. SELECT REPLACE(v,' ',',') FROM
  73.     (SELECT CAST('<r>' + REPLACE(@s,',','</r><r>') + '</r>' AS XML) x) a
  74. CROSS APPLY
  75.     (SELECT CAST(a.x.query('distinct-values(//r)') AS VARCHAR(MAX)) v) b  --直接调用distinct-values函数来操作
  76. /*
  77. a,b,c,dd,ee,f,aa
  78. */
  79. -- 导入去重, last() , position()
  80. DECLARE   @doc  xml
  81. SET   @doc   ='<?xml version="1.0" encoding="gb2312" ?>
  82. <employees>
  83.     <employee>
  84.         <empid>e0001</empid>
  85.         <name>萧峰</name>
  86.     </employee>
  87.     <employee>
  88.         <empid>e0002</empid>
  89.         <name>段誉</name>
  90.     </employee>
  91.     <employee>
  92.         <empid>e0003</empid>
  93.         <name>王语嫣</name>
  94.     </employee>
  95.     <employee>
  96.         <empid>e0003</empid>
  97.         <name>张无忌</name>
  98.     </employee>
  99. </employees>
  100. '
  101. create table people2 
  102.     personid varchar(10)  primary key ,
  103.     name varchar(20) 
  104.  )
  105. INSERT people2
  106. SELECT DISTINCT b.* FROM
  107.     (SELECT x = @doc.query('for $e in //employee  return  //employee[empid = $e/empid][last()]')) a  --FLWOR时,用当前节点去//emploee节点集中找节点集中empid等于当前节点的empid, 在找到的集合中取最后一个利用last()函数
  108. CROSS APPLY
  109.     (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
  110. SELECT * FROM people2
  111. /*
  112. e0001    萧峰
  113. e0002    段誉
  114. e0003    张无忌
  115. */
  116. GO
  117. drop table people2 
  118. GO
  119. --同组一选多,也可应用此方法,不过没有必要,就不再累赘了。
  120. --(3)
  121. --====================================================================
  122. --列名,列值相关
  123. --a,按行聚合
  124. declare @t table(Sname nvarchar(5),  V1 float,    V2 float,    V3 float,      V4 float,    V5 float,      V6 float) 
  125. insert @t select N'张三',    0.11 , 0.21 , 0.29,  0.32 ,   0.11,    0.08 
  126. insert @t select N'李四',    0.01 , 0.61 , 0.21,  0.73 ,   0.21,    0.12 
  127. insert @t select N'张五',    0.31 , 0.21 , 0.23,  0.33 ,   0.91,    0.65 
  128. insert @t select N'张六',    0.59 , 0.11,  0.26,  0.13,    0.01,    0.15 
  129. select b.* from
  130.     (select x=cast((select * from @t for xml path('r')) as xml)) a
  131. cross apply
  132.     (
  133.         select name=x.query('./Sname/text()'),v=x.query('max(./*[local-name(.)!="Sname"])') from a.x.nodes('//r') as t(x)  
  134.         --r为二级节点(因为文档本身无根节点,即为每项的顶级节点)即为一个r节点表示一条记录. r下级节点,每个表示一个列,因为列名未知,所以用/*匹配所有节点,因为name为区别列,不参与聚合运算,故用local-name取得来过滤
  135.     ) b
  136. /*
  137. 张三    0.32
  138. 李四    0.73
  139. 张五    0.91
  140. 张六    0.59
  141. */
  142. --b ,由值引到取列
  143. if not object_id('T1') is null
  144.     drop table T1
  145. GO
  146. Create table T1([tId] int,[tName] nvarchar(4))
  147. Insert T1
  148. select 1,N'zhao' union all
  149. select 2,N'qian' union all
  150. select 3,N'sun'
  151. Go
  152. --> --> 借且(Roy)生成測試數據
  153.  
  154. if not object_id('T2') is null
  155.     drop table T2
  156. Go
  157. Create table T2([tId] int,[zhao] nvarchar(1),[qian] nvarchar(1),[sun] nvarchar(1))
  158. Insert T2
  159. select 1,N'a',N'b',N'c' union all
  160. select 2,N'd',N'e',N'f' union all
  161. select 3,N'g',N'h',N'i'
  162. Go
  163. SELECT c.tid,c.tName,v FROM t1 c
  164. CROSS APPLY
  165.     (SELECT x=(SELECT * FROM t2 WHERE tid=c.tid FOR XML PATH('r'),TYPE)) a
  166. CROSS APPLY
  167.     (SELECT v=t.x.query('./*[local-name(.)=xs:string(sql:column("c.tName")) ]/text()') 
  168.         FROM a.x.nodes('//r') AS t(x)
  169.     ) b
  170. /*
  171. 1    zhao    a
  172. 2    qian    e
  173. 3    sun    i
  174. */
  175. --c, 列名,列值,与系统表 
  176. CREATE TABLE tb(f1 INT,f2 INT,x INT,z INT,d INT,ex INT,dd INT,vv INT) 
  177. INSERT tb SELECT 1,2,3,5,11,3,2423,33 
  178. GO 
  179. SELECT * FROM tb 
  180. GO 
  181. SELECT name,v FROM
  182.   ( SELECT name FROM sys.columns WHERE object_idobject_id=object_id('tb','u') ) a 
  183. CROSS JOIN
  184.   (SELECT x=(SELECT * FROM tb FOR XML PATH('r'),TYPE)) b 
  185. CROSS APPLY
  186.  (SELECT v=t.x.query('./*[local-name(.)=xs:string(sql:column("a.name")) ]/text()') FROM b.x.nodes('//r') AS t(x) ) c 
  187. /*
  188. f1  1
  189. f2  2
  190. x   3
  191. z   5
  192. d   11
  193. ex  3
  194. dd  2423
  195. vv  33
  196. */
  197. GO 
  198. DROP TABLE tb
  199. GO
  200. --(4)
  201. --一些综合计算
  202. --以下表 ta.a值 yyyymmdd-yyyymmdd表连续时间段,","表单个日期
  203. If object_id('ta','u') is not null 
  204.     Drop table ta
  205. Go
  206. Create table ta(a varchar(100))
  207. Go
  208. Insert into ta
  209. select '1 ¦ ¦20080101-20080911' 
  210. union all
  211. select '2 ¦ ¦20080101,20080201,20080301,20080515,20080808'
  212. union all
  213. select '3 ¦ ¦20080101,20080201,20080301,20080515,20081108'
  214. Go
  215. declare @s varchar(8)
  216. select @sconvert(varchar(8),getdate(),112)
  217. select stuff(replace(replace(cast(x as varchar(1000)),'</item><item>',case when type='1' then '-' else ',' end),'</item>',''),1,6,type + ' ¦ ¦') a
  218.     from
  219.     (
  220.         select left(a,1) type, 
  221.             cast(
  222.                     '<item>
  223.                     + 
  224.                     replace(
  225.                         stuff(a,1,5,''),
  226.                         case when left(a,1)=1 then '-' else ',' end,
  227.                         '</item><item>'
  228.                         )
  229.                     + 
  230.                     '</item>'
  231.                 AS XML
  232.                 ) x
  233.         from ta
  234.     ) base
  235.     where x.value('
  236.             if (sql:column("base.type")="1") then
  237.                 if(
  238.                     (/item/text())[1]<sql:variable("@s")
  239.                     and
  240.                     (/item/text())[2]>sql:variable("@s")
  241.                 )
  242.                 then 1
  243.                 else 0
  244.             else
  245.                 count(//item[text()>sql:variable("@s")])
  246.             '
  247.             ,
  248.             'int'
  249.             )>0
  250. go
待续..

你可能感兴趣的:(SQLServer2005 XML在T-SQL查询中的典型应用)