你的问题还不是很清楚...
据我了解,您有各种各样的不同 XML,并且您想通用地阅读它们。如果这是真的,我建议您的下一个问题,在您的示例数据中反映这一点。
一个普遍的说法是:如果您想动态设置结果集的 descriptive 元素(在这种情况下:列的名称),则无法绕过动态创建的语句. 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(实体属性值)列表
--在这个结果中,您可以在自己的行中获得每个属性
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() 或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并检查结果。