You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

SQL XML查询脚本逻辑解析求助:解读含FOR XML PATH、ELEMENTS XSINIL等函数的语句

你的SQL脚本逻辑全解析

嘿,我来一步步帮你拆解这个脚本,把你的疑问全搞定!

先搞懂整个脚本的核心作用

这个脚本的目的是把dbo.tablename表的所有数据,转换成**一行表头(列名用^^分隔) + 每一行数据(列值用^^分隔)**的格式,最终输出的每一行都是一个用^^串联的字符串。


逐个解答你的疑问

1. 仅单表为啥要用CROSS APPLY?

这里的CROSS APPLY不是用来关联两个物理表的,它是处理XML数据的特殊技巧!
前面的select T.* for xml path(''), type, ELEMENTS XSINIL已经把原表的一行数据转换成了XML格式(比如<列名1>值1</列名1><列名2>值2</列名2>),得到的T1.N是一个XML类型的列。CROSS APPLY配合nodes()函数,能把这个XML里的每个子节点(对应原表的每一列)拆成单独的行,这样我们才能逐个处理每个列名或列值,最后再把它们拼接成一个完整的字符串。

2. 表中没有nodes列,“nodes”来自哪儿?

nodes不是表的列,它是XML数据类型的内置函数
写法T1.N.nodes('/*')里,T1.N是XML类型的列,.nodes()函数的作用是:根据括号里的XPath表达式(这里是/*),把XML中匹配的节点转换成一个行集。后面的as T2(N)是给这个行集起别名T2,行集里的列叫N,这个N就是每个XML节点的对象。

3. fn:local-name(.)[1]是什么意思?

这是XQuery的函数,用来获取XML节点的本地名称(也就是节点的名字,对应原表的列名):

  • fn:local-name():函数本身,专门提取XML节点的名称
  • (.):代表当前正在处理的节点(就是T2.N对应的那个列节点)
  • [1]:取第一个匹配的结果(这里每个节点只有一个名称,加不加都不影响,但写法更严谨)
    所以整个表达式就是获取当前XML节点的名字,也就是原表的列名

4. T1.N.Nodes('/*')具体含义?

拆解一下:

  • T1.N:是前面select T.* for xml path(''), type, ELEMENTS XSINIL生成的XML列,比如原表有idname两列,这个XML大概是<id>1</id><name>张三</name>
  • .nodes('/*')/*是XPath表达式,意思是“匹配XML里的所有顶层节点”(也就是每个列对应的节点)
    所以T1.N.Nodes('/*')就是把这个XML里的每个列节点,拆成单独的行,每一行对应一个列的XML节点,方便后续逐个处理。

脚本两部分的详细拆解

第一部分:生成表头(列名)

select top 1 
( select '^^'+T2.N.value('fn:local-name(.)[1]', 'varchar(max)') 
  from ( select T.* for xml path(''), type, ELEMENTS XSINIL ) as T1(N) 
  cross apply T1.N.nodes('/*') as T2(N) 
  for xml path(''), type, ELEMENTS XSINIL 
).value('substring((.)[1], 3)', 'varchar(max)') as systemname 
from dbo.tablename as T with (nolock)
  1. 先取表的第一行数据top 1,把它转换成XML格式,存在T1.N
  2. CROSS APPLY把XML的每个列节点拆成行,每个行对应一个列的XML节点
  3. 给每个列名前面加^^,然后用for xml path('')把所有带^^的列名拼接成一个XML字符串(比如^^id^^name^^age
  4. 最后用.value('substring((.)[1], 3)', ...)去掉开头的两个^,得到id^^name^^age,作为结果的第一行(表头)

第二部分:生成每一行的列值

UNION ALL 
select 
( select '^^'+ISNULL(T2.N.value('./text()[1]', 'varchar(max)'),'') 
  from ( select T.* for xml path(''), type, ELEMENTS XSINIL ) as T1(N) 
  cross apply T1.N.nodes('/*') as T2(N) 
  for xml path(''), type, ELEMENTS XSINIL 
).value('substring(./text()[1], 3)', 'varchar(max)') 
from dbo.tablename as T with (nolock)
  1. 对表的每一行数据,转换成XML格式
  2. 同样拆成每个列节点,然后取每个节点的文本值(./text()[1]就是节点里的内容,也就是列值),如果为空就替换成空字符串,前面加^^
  3. 拼接成带^^的字符串后,去掉开头的两个^,得到比如1^^张三^^25这样的行,和表头拼在一起输出。

内容的提问来源于stack exchange,提问作者Kath Chua

火山引擎 最新活动