sql - 在 T-SQL SQL-SERVER 中将单个 XML 括号列转换为多列
问题描述
我见过与我类似的问题,但 XML 格式一直不同,因为我拥有的 XML 格式不遵循“标准”结构。我的表如下所示(以 XML 括号作为行值的单列):
|VAL|
|<person name="bob" age="22" city="new york" occupation="student"></person>|
|<person name="bob" age="22" city="new york" occupation="student"></person>|
我正在寻找的结果是:
|Name|age|city |occupation|
|bob |22 |new york|student |
|bob |22 |new york|student |
我可以使用这些列名创建硬编码脚本,但问题是我有 20 多个表,这些表都需要自定义脚本。我的想法是,有一种方法可以让我动态地考虑到目标表和源表(xml),我可以有一个生成这些数据的过程。
解决方案
你的问题不是很清楚...
据我了解,您有各种不同的XML,并且您希望通用地阅读它们。如果这是真的,我建议你的下一个问题,在你的样本数据中反映这一点。
一种通用的说法是:如果您想动态地设置结果集的描述性元素(在这种情况下:列的名称),则无法绕过动态创建的语句。T-SQL 依赖于一些你必须提前知道的事情。
尝试这个:
我设置了一个模型场景来模拟您的问题(请在您的下一个问题中尝试自己这样做):
DECLARE @tbl TABLE(ID INT IDENTITY, Descr VARCHAR(100), VAL XML);
INSERT INTO @tbl VALUES
('One person',N'|<person name="bob" age="22" city="new york" occupation="student"></person>')
,('One more person','<person name="bob" age="22" city="new york" occupation="student"></person>')
,('One country','<country name="Germany" capital="Berlin" continent="Europe"></country>');
--此查询依赖于预先知道的所有可能的属性。
--common 属性,如name
,为个人和国家
返回 --differing 属性返回为 NULL。-- 一个优点可能是,如果合适,您可以使用特定的数据类型。
SELECT t.ID
,t.Descr
,t.VAL.value('(/*[1]/@name)[1]','nvarchar(max)') AS [name]
,t.VAL.value('(/*[1]/@age)[1]','nvarchar(max)') AS [age]
,t.VAL.value('(/*[1]/@city)[1]','nvarchar(max)') AS [city]
,t.VAL.value('(/*[1]/@occupation)[1]','nvarchar(max)') AS [occupation]
,t.VAL.value('(/*[1]/@city)[1]','nvarchar(max)') AS [city]
,t.VAL.value('(/*[1]/@capital)[1]','nvarchar(max)') AS [capital]
,t.VAL.value('(/*[1]/@continent)[1]','nvarchar(max)') AS [continent]
FROM @tbl t;
--此查询以经典 EAV (entity-attribute-value) 列表形式返回 --
在此结果中,您可以在自己的行中获取每个属性
SELECT t.ID
,t.Descr
,A.attrs.value('local-name(.)','nvarchar(max)') AS AttrName
,A.attrs.value('.','nvarchar(max)') AS AttrValue
FROM @tbl t
CROSS APPLY t.VAL.nodes('/*[1]/@*') A(attrs);
这两种方法都可以作为字符串级别的语句生成,然后由EXEC()
or执行sp_executesql
。
提示:一种方法可能是将 EAV 列表插入到一个容错的临时表中,然后从那里进行条件聚合或PIVOT
硬编码的 VIEW。
动态方法
为了读取<person>
元素,我们需要这个:
SELECT t.ID
,t.Descr
,t.VAL.value('(/*[1]/@name)[1]','nvarchar(max)') AS [name]
,t.VAL.value('(/*[1]/@age)[1]','nvarchar(max)') AS [age]
,t.VAL.value('(/*[1]/@city)[1]','nvarchar(max)') AS [city]
,t.VAL.value('(/*[1]/@occupation)[1]','nvarchar(max)') AS [occupation]
FROM @tbl t
WHERE VAL.value('local-name(/*[1])','varchar(100)')='person';
我们所要做的就是生成变化的部分:
尝试这个:
带有真实桌子的新模型
CREATE TABLE SimulateYourTable(ID INT IDENTITY, Descr VARCHAR(100), VAL XML);
INSERT INTO SimulateYourTable VALUES
('One person',N'|<person name="bob" age="22" city="new york" occupation="student"></person>')
,('One more person','<person name="bob" age="22" city="new york" occupation="student"></person>')
,('One country','<country name="Germany" capital="Berlin" continent="Europe"></country>');
--过滤<person>
实体
DECLARE @entityName NVARCHAR(100)='person';
--这是一个代表命令的字符串
DECLARE @cmd NVARCHAR(MAX)=
'SELECT t.ID
,t.Descr
***columns here***
FROM SimulateYourTable t
WHERE VAL.value(''local-name(/*[1])'',''varchar(100)'')=''***name here***''';
--有了这个我们可以创建所有的列
--提示:使用 SQL Server 2017+ 有STRING_AGG()
- 更简单!
DECLARE @columns NVARCHAR(MAX)=
(
SELECT CONCAT(',t.VAL.value(''(/*[1]/@',Attrib.[name],')[1]'',''nvarchar(max)'') AS ',QUOTENAME(Attrib.[name]))
FROM SimulateYourTable t
CROSS APPLY t.VAL.nodes('//@*') AllAttrs(a)
CROSS APPLY (SELECT a.value('local-name(.)','varchar(max)')) Attrib([name])
WHERE VAL.value('local-name(/*[1])','varchar(100)')=@entityName
GROUP BY Attrib.[name]
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)');
--现在我们把它塞进我们的命令中
SET @cmd=REPLACE(@cmd,'***columns here***',@columns);
SET @cmd=REPLACE(@cmd,'***name here***',@entityName);
——这是命令。
--提示:您可以使用它来创建物理视图,而无需键入它们...
PRINT @cmd;
您可以使用EXEC(@cmd)
此动态 SQL 来执行并检查结果。
推荐阅读
- c++ - 我在 Visual Studio 中使用 MSVC(Microsoft 编译器),我想用 clang 或 GCC 编译?
- rust - 如何测试 RAII?
- php - 我如何使 php 按值名称排序
- php - FOREACH 具有来自数组的特定键
- c# - 未在操作方法上声明时如何从提交的表单中获取值?
- c# - GitHub Octokit GitHubClient.Repository.Content.GetAllContents 永远不会返回
- javascript - 为什么结果不一样??JavaScript
- text - iOS 14 Text() 带天数计时器
- python - 我该如何做到,如果 XPATH 在网站上没有看到元素,它会跳过该行并移动到下一行?
- r - R中的动态函数名称