Конкатенация строк с FOR XML, но с несколькими столбцами? - PullRequest
6 голосов
/ 13 октября 2011

Я часто использую такие запросы, как:

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 часто представляет собой длинный список объединяемых таблиц, поэтому он также может влиять на производительность, получая одни и те же таблицы снова и снова.

Есть ли лучший способ сделать это?

Спасибо.

Ответы [ 3 ]

8 голосов
/ 30 апреля 2014

Вы могли бы сделать что-то вроде этого. Вместо немедленной отправки значения XML в строку этот запрос использует ключевое слово TYPE для возврата объекта типа xml, который затем может быть запрошен. Три функции запроса выполняют поиск в объекте xml всех экземпляров элемента Somefield и возвращают новый объект xml, содержащий только эти значения. Затем функция value удаляет теги xml, окружающие значения, и передает их в varchar (max)

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]
3 голосов
/ 13 октября 2011

Вы можете создать Определяемую пользователем статистическую функцию CLR , которая выполнит конкатенацию за вас.

Ваш код будет выглядеть следующим образом.

select S.ID,
       dbo.Concat(S.SomeField1), 
       dbo.Concat(S.SomeField2),
       dbo.Concat(S.SomeField3)
from SomeTable as S
group by S.ID
2 голосов
/ 10 января 2016

Это тот же ответ, который я дал здесь: https://dba.stackexchange.com/questions/125771/multiple-column-concatenation/

ОП этого вопроса ссылается на ответ, данный здесь.Ниже вы можете увидеть, что иногда самый простой ответ может быть лучшим.Если SomeTable - это несколько таблиц, я бы пошел дальше и поместил его в CTE, чтобы избежать многократного повторения одного и того же сложного кода.

Я провел несколько тестов, используя чуть более 6 миллионов строк.С индексом для столбца идентификатора.

Вот что я придумал.

Ваш первоначальный запрос:

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 

Этот запрос выполнялся в течение ~ 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 минуты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...