3

SQL Server 2008 に次の XML を持つ 2 つの 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>

@Second基本的に、2 つの XML 変数の内容を、変数が優先される 1 つにマージしたいと考えています( @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

このクエリにより、1 つの列にすべての要素が含まれ、別の列に要素のデータが含まれるテーブルが得られます。次のように最終的な 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 シュレッディング レベルでこれに対処できるかもしれませんが、ここでは、更新されたクエリに対して動的なピボットを実行します。動的 SQL からアクセスできるように、FinalValues をステージング テーブルに書き込むことから始めます。

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 に答える