2011-10-13 7 views
6

FOR XMLとの行連結ですが、複数の列がありますか?

私はしばしば以下のようなクエリを使います:

SELECT * 
FROM ThisTable 
     OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField) A 

しばしば、ただ一つではなく、このテーブルから複数の連結された連結フィールドを得たいと思う。私は論理的にこれを行うことができます:

SELECT * 
FROM ThisTable 
     OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField) A 
     OUTER APPLY (SELECT (SELECT SomeField2 + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField2) B 
     OUTER APPLY (SELECT (SELECT SomeField3 + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField3) C 

しかし、何かが更新される必要があるときは、また、SomeTableはしばしばジョインされたテーブルの長いリストであるため、同じテーブルを何度も何度も繰り返してもパフォーマンスに影響を与える可能性があります。

これを行う良い方法はありますか?

ありがとう。

答えて

8

このようなことが可能です。すぐに文字列にXML値を送信するのではなく、このクエリはTYPEキーワードを使用してxml型オブジェクトを返します。このオブジェクトはクエリできます。 3つのクエリ関数は、Somefield要素のすべてのインスタンスについてxmlオブジェクトを検索し、それらの値だけを含む新しいxmlオブジェクトを返します。値関数が値を囲むXMLタグを取り除き、varchar型(最大)に渡し

SELECT ThisTable.ID 
     ,[A].query('/Somefield').value('/', 'varchar(max)') AS [SomeField_Combined] 
     ,[A].query('/Somefield2').value('/', 'varchar(max)') AS [SomeField2_Combined] 
     ,[A].query('/Somefield3').value('/', 'varchar(max)') AS [SomeField3_Combined] 
FROM ThisTable 
     OUTER APPLY (
        SELECT (
          SELECT SomeField + ' ' AS [SomeField] 
            ,SomeField2 + ' ' AS [SomeField2] 
            ,SomeField3 + ' ' AS [SomeField3] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR 
          XML PATH('') 
           ,TYPE 
          ) AS [A] 
        ) [A] 
+0

良いアイデアだが性能が良くないhttp://dba.stackexchange.com/q/125771/3690 –

3

連結を行うCLR User-Defined Aggregate Functionを作成することができます。

あなたのコードは、代わりに次のようになります。

select S.ID, 
     dbo.Concat(S.SomeField1), 
     dbo.Concat(S.SomeField2), 
     dbo.Concat(S.SomeField3) 
from SomeTable as S 
group by S.ID 
2

私はここに与えたとして、これは同じ答えである:その質問のhttps://dba.stackexchange.com/questions/125771/multiple-column-concatenation/

OPを参照しましたここで与えられた答え。あなたは時には最も簡単な答えが最高になることがあることを以下に見てきます。 SomeTableが複数のテーブルである場合は、同じ複雑なコードを複数回使用することを避けるために、先に進んでCTEに入れます。

私はわずか6ミルの行を使っていくつかのテストを実行しました。 ID列にインデックスを付けます。

これは私が思いついたものです。

あなたの最初のクエリ:

SELECT * FROM (
    SELECT t.id, 
      stuff([M].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1], 
      stuff([M].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2] 
    FROM dbo.test t 
    OUTER APPLY(SELECT (
        SELECT id, ','+name AS name 
        ,','+car AS car 
        FROM test WHERE test.id=t.id 
        FOR XML PATH('') ,type) 
       AS M) 
      M) S 
GROUP BY id, SomeField_Combined1, SomeField_Combined2 

この1つは〜23分間走りました。

私が最初に学んだバージョンであるこのバージョンを実行しました。いくつかの点では、時間がかかるはずだがそうではないようだ。

SELECT test.id, 
    STUFF((SELECT ', ' + name 
      FROM test ThisTable 
      WHERE test.id = ThisTable.id 
      FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField, 
    STUFF((SELECT ', ' + car 
      FROM test ThisTable 
      WHERE test.id = ThisTable.id 
      FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField2 
FROM test 
GROUP BY id 

このバージョンはわずか2分で実行されました。

関連する問題