3

假设我在 SQL Server 2008 中有 2 个 XML 变量,并带有以下 XML

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>

基本上,我想将 2 个 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

任何帮助高度赞赏。

更新:我将其归结为以下查询:

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

3 回答 3

5
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')
于 2012-06-20T18:25:04.943 回答
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>
于 2012-06-20T17:45:57.190 回答
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>
')
于 2012-07-18T10:00:32.363 回答