2012-06-20 2 views
2

Давайте предположим, что у меня есть 2 XML переменные в SQL Server 2008 со следующим XMLКак объединить 2 XML-переменные в SQL Server 2008

DECLARE @FIRST XML = '<DBPerson> 
          <firstname>John</firstname> 
          <lastname>Bob</lastname> 
         </DBPerson>', 
     @Second XML = '<FromUI> 
          <lastname>New Bob</lastname> 
          <age>39</age> 
         </FromUI>'; 

Я хочу следующий вывод:

<DBPerson> 
    <firstname>John</firstname> 
    <lastname>New Bob</lastname> 
    <age>39</age> 
</DBPerson> 

В основном я хотите объединить содержимое двух XML-переменных в один, где переменная @Second должна иметь приоритет (если узел существует как в @First & @Second, следует учитывать узел внутри @Second).

Подход, который я взял, чтобы сначала получить список всех уникальных элементов в обоих корневых элементов, как это:

WITH ALLFields AS 
(
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @Second.nodes('FromUI/*') AS x(y) 
    UNION 
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @FIRST.nodes('DBPerson/*') AS x(y) 
) 
SELECT * FROM ALLFields AF 

Но я невежествен, как действовать отсюда на. Я знаю, что я должен использовать sql:column где-то построить таблицу первой, чтобы получить только узловые имена и их значение (на основе AllFields), а затем я могу использовать FOR XML PATH('DBPerson'), чтобы сформировать окончательный XML, но немного не уверен в использовании sql:column

Любая помощь высоко ценится.

UPDATE: Я варил вниз к следующему запросу:

DECLARE @FIRST XML = '<DBPerson><firstname>John</firstname><lastname>Bob</lastname></DBPerson>', 
     @Second XML = '<FromUI><lastname>New Bob</lastname><age>39</age></FromUI>'; 


WITH ALLFields AS 
(
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @Second.nodes('FromUI/*') AS x(y) 
    UNION 
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @FIRST.nodes('DBPerson/*') AS x(y) 
), Filtered AS 
(
    SELECT 
      Element 
     , @FIRST.value('(DBPerson/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS F 
     , @Second.value('(FromUI/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS S 
    FROM ALLFields AF 
), FinalValues AS 
(
    SELECT 
     Element 
     , CASE 
       WHEN S IS NULL THEN F 
       ELSE S 
      END AS V 
    FROM Filtered 
) 
SELECT * FROM FinalValues 

Этот запрос дает мне таблицу со всеми элементами в одном столбце, и данные для элементов в другом столбце. Теперь, как я генерировать свой окончательный XML, как это:

<DBPerson><firstname>John</firstname><lastname>New Bob</lastname><age>39</age></DBPerson> 

ответ

4
select isnull(S.N.query('.'),F.N.query('.')) as '*' 
from @First.nodes('/DBPerson/*') as F(N) 
    full outer join @Second.nodes('/FromUI/*') as S(N) 
    on F.N.value('local-name(.)', 'nvarchar(100)') = S.N.value('local-name(.)', 'nvarchar(100)') 
for xml path(''), root('DBPerson') 
+0

Удивительный .. Ты мужчина – Wiz

1

Кто-то, кто знает XML измельчения лучше, чем я мог бы решить эту проблему на уровне XML шинковки, но здесь я буду выполнять динамический стержень на свой обновленный запрос. Я начну писать FinalValues ​​в таблицу промежуточной, поэтому он может получить доступ к динамическим SQL:

DECLARE @FIRST XML = '<DBPerson><firstname>John</firstname><lastname>Bob</lastname></DBPerson>', 
     @Second XML = '<FromUI><lastname>New Bob</lastname><age>39</age></FromUI>'; 


WITH ALLFields AS 
(
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @Second.nodes('FromUI/*') AS x(y) 
    UNION 
    SELECT 
      x.y.value('local-name(.)','varchar(50)') As Element 
    FROM @FIRST.nodes('DBPerson/*') AS x(y) 
), Filtered AS 
(
    SELECT 
      Element 
     , @FIRST.value('(DBPerson/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS F 
     , @Second.value('(FromUI/*[local-name()=sql:column("Element")])[1]','varchar(max)') AS S 
    FROM ALLFields AF 
), FinalValues AS 
(
    SELECT 
     Element 
     , CASE 
       WHEN S IS NULL THEN F 
       ELSE S 
      END AS V 
    FROM Filtered 
) 
SELECT [age],[firstname],[lastname] FROM 
    FinalValues 
PIVOT (
    MAX(V) 
    FOR Element IN ([age],[firstname],[lastname]) 
) AS p 
FOR XML PATH('DBPerson'); 

/* Commenting out the dynamic piece to show the straight pivot above 
SELECT * 
INTO FinalValues_Staging 
FROM FinalValues; 
GO 

DECLARE @sql NVARCHAR(MAX) 
, @col NVARCHAR(MAX); 

SELECT @col = COALESCE(@col, '') + QUOTENAME(Element) + ',' 
FROM 
(
     SELECT DISTINCT Element 
     FROM FinalValues_Staging 
) AS x; 
SET @col = LEFT(@col, LEN(@col)-1); 
SET @sql = N'SELECT <COL> FROM 
    dbo.FinalValues_Staging 
PIVOT (
    MAX(V) 
    FOR Element IN (<COL>) 
) AS p 
FOR XML PATH(''DBPerson'')'; 

SET @sql = REPLACE(@sql, '<COL>', @col); 
EXEC sp_executeSQL @sql; 
PRINT @sql; 
GO 

-- DROP TABLE FinalValues_Staging 
*/ 

Результат:

<DBPerson> 
    <age>39</age> 
    <firstname>John</firstname> 
    <lastname>New Bob</lastname> 
</DBPerson> 
+0

Не существует способа, которым нам не нужно использовать генерацию строк динамической строки SQL? Я намерен сохранить возвращенный xml в переменной xml в одной и той же хранимой процедуре и обновить столбец в БД с помощью этого xml – Wiz

+0

Да, вы можете просто заменить окончательный запрос на сводный запрос, как показано в моем редактировании. Тот же результат, только с жестко закодированными столбцами. –

+0

Но нет определенного списка узлов, таких как firstname, lastname, age и т. Д. Я имею в виду, что имена узлов, содержащие 2 xml, являются динамическими. – Wiz

1

Вы можете использовать XQuery, чтобы вернуть первый элемент последовательность, например

DECLARE @first XML = '<DBPerson> 
          <firstname>John</firstname> 
          <lastname>Bob</lastname> 
         </DBPerson>', 
     @second XML = '<FromUI> 
          <lastname>New Bob</lastname> 
          <age>39</age> 
         </FromUI>'; 

DECLARE @xml XML 

-- Combine the two XMLs 
SET @xml = (SELECT @first AS "*", @second AS "*" FOR XML PATH('')) 

SELECT @xml.query('<DBPerson> 
    {(FromUI/firstname, DBPerson/firstname)[1]} 
    {(FromUI/lastname, DBPerson/lastname)[1]} 
    {(FromUI/age, DBPerson/age)[1]} 
</DBPerson> 
') 
Смежные вопросы