Dav*_*.id 7 xml t-sql sql-server pivot xquery-sql
我正在使用SQL Server(2008/2012),我知道很多搜索都有类似的答案,但我似乎无法为我的案例找到合适的示例/指针.
我在SQL Server表中有一个XML列来保存这些数据:
<Items>
<Item>
<FormItem>
<Text>FirstName</Text>
<Value>My First Name</Value>
</FormItem>
<FormItem>
<Text>LastName</Text>
<Value>My Last Name</Value>
</FormItem>
<FormItem>
<Text>Age</Text>
<Value>39</Value>
</FormItem>
</Item>
<Item>
<FormItem>
<Text>FirstName</Text>
<Value>My First Name 2</Value>
</FormItem>
<FormItem>
<Text>LastName</Text>
<Value>My Last Name 2</Value>
</FormItem>
<FormItem>
<Text>Age</Text>
<Value>40</Value>
</FormItem>
</Item>
</Items>
Run Code Online (Sandbox Code Playgroud)
因此,即使结构<FormItem>相同,我也可以拥有多个(通常不超过20-30个)表单项.
我本质上是尝试以下面的格式从SQL返回一个查询,即基于/ FormItem/Text的动态列:
FirstName LastName Age ---> More columns as new `<FormItem>` are returned
My First Name My Last Name 39 Whatever value etc..
My First Name 2 My Last Name 2 40
Run Code Online (Sandbox Code Playgroud)
所以,目前我有以下内容:
select
Tab.Col.value('Text[1]','nvarchar(100)') as Question,
Tab.Col.value('Value[1]','nvarchar(100)') as Answer
from
@Questions.nodes('/Items/Item/FormItem') Tab(Col)
Run Code Online (Sandbox Code Playgroud)
当然,这还没有调换我的XML行转换成列,显然是无论如何固定领域..我一直在尝试不同的"动态SQL"的方法,其中SQL执行的不同的选择(对我来说)的<Text>节点,然后使用某种Pivot?但我似乎无法找到神奇的组合来返回我需要的结果作为每行(<Item>在集合中<Items>)的动态列集.
我确信看到这么多非常相似的例子可以做到,但是我的解决方案再次出现了!
任何帮助感激不尽!
解析XML相当昂贵,因此不是解析一次来构建动态查询,而是一次获取数据,您可以创建一个带有Name-Value列表的临时表,然后将其用作动态数据透视查询的源.
dense_rank是否有创建ID来转动.
要在动态查询中构建列列表,它将使用该for xml path('')技巧.
此解决方案要求您的表具有主键(ID).如果您在变量中包含XML,则可以稍微简化一下.
select dense_rank() over(order by ID, I.N) as ID,
F.N.value('(Text/text())[1]', 'varchar(max)') as Name,
F.N.value('(Value/text())[1]', 'varchar(max)') as Value
into #T
from YourTable as T
cross apply T.XMLCol.nodes('/Items/Item') as I(N)
cross apply I.N.nodes('FormItem') as F(N)
declare @SQL nvarchar(max)
declare @Col nvarchar(max)
select @Col =
(
select distinct ','+quotename(Name)
from #T
for xml path(''), type
).value('substring(text()[1], 2)', 'nvarchar(max)')
set @SQL = 'select '+@Col+'
from #T
pivot (max(Value) for Name in ('+@Col+')) as P'
exec (@SQL)
drop table #T
Run Code Online (Sandbox Code Playgroud)