Ваш вопрос не совсем понятен ...
Насколько я понимаю, у вас есть множество различных 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>');
- этот запрос опирается на все возможные заранее известные атрибуты.
- общие атрибуты, такие как name
, возвращаются для человека и для страны
- отличающиеся атрибуты возвращаются как NULL. - Одним из преимуществ может быть то, что вы можете использовать указанный тип данных c, если это необходимо.
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()
или sp_executesql
.
Подсказка: один из подходов может состоять в том, чтобы вставить список EAV в допустимую промежуточную таблицу и перейти оттуда к условной агрегации , PIVOT
или жестко закодированным представлениям.
Dynami c подхода
Чтобы прочитать элементы <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***''';
- с помощью этого мы можем создать все столбцы
--Hin t: С 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);
- Это команда.
- Подсказка: вы можете использовать это для создания физических VIEW без необходимости набирать их в ...
PRINT @cmd;
Вы можете использовать EXEC(@cmd)
, чтобы выполнить эту динамику c SQL и проверьте результат.