SQL-запрос - объединение результатов в одну строку

У меня есть функция sql, которая включает этот код:

DECLARE @CodeNameString varchar(100)

SELECT CodeName FROM AccountCodes ORDER BY Sort

Мне нужно объединить все результаты запроса выбора в CodeNameString.

Очевидно, что цикл FOREACH в коде C # сделает это, но как мне это сделать в SQL?


person Matthew Jones    schedule 04.03.2011    source источник
comment
Какая версия SQL Server ??   -  person marc_s    schedule 04.03.2011
comment
много раз отвечал ... но будьте осторожны, не все реализации конкатенации FOR XML PATH будут правильно обрабатывать специальные символы XML (‹, &,› и т. д.), как мой ответ на предыдущий ответ: http://stackoverflow.com/questions/5031204/does-t-sql-have-an-aggregate-function-to-concatenate-strings/5031297#5031297   -  person KM.    schedule 04.03.2011


Ответы (6)


Если вы используете SQL Server 2005 или новее, вы можете использовать этот FOR XML PATH & STUFF трюк:

DECLARE @CodeNameString varchar(100)

SELECT 
   @CodeNameString = STUFF( (SELECT ',' + CodeName 
                             FROM dbo.AccountCodes 
                             ORDER BY Sort
                             FOR XML PATH('')), 
                            1, 1, '')

FOR XML PATH('') в основном объединяет ваши строки в один длинный результат XML (что-то вроде ,code1,code2,code3 и т. Д.), А STUFF помещает символ «ничего» в первый символ, например стирает "лишнюю" первую запятую, чтобы дать вам результат, который вы, вероятно, ищете.

ОБНОВЛЕНИЕ: ОК - я понимаю комментарии - если ваш текст в таблице базы данных уже содержит символы типа <, > или &, то мое текущее решение фактически закодирует их в &lt;, &gt; и &amp;.

Если у вас есть проблема с этой кодировкой XML - тогда да, вы должны посмотреть на решение, предложенное @KM, которое также работает для этих символов. Одно слово предупреждения от меня: этот подход требует гораздо больше ресурсов и обработки - просто чтобы вы знали.

person marc_s    schedule 04.03.2011
comment
при этом НЕ будут должным образом обрабатываться специальные символы XML (<, &, > и т. д.), как в моем предыдущем ответе, см. здесь: stackoverflow.com/questions/5031204 / - person KM.; 04.03.2011
comment
@marc_s Это опасно. Я должен повторить комментарий К.М. выше. - person jnm2; 30.03.2016
comment
@ jnm2: согласовано - если кому-то нужны эти специальные символы, то решение от KM - это то, что нужно. ОДНАКО: это НАМНОГО более интенсивная обработка, поэтому я бы рекомендовал использовать его только, если вам действительно нужны эти несколько специальных символов XML в вашем вывод - person marc_s; 30.03.2016
comment
@marc_s Как можно заранее узнать, появятся ли эти персонажи в будущем в большинстве случаев? Делать предположение, что они вам не понадобятся, - вот что опасно. Можете ли вы показать документацию о том, что метод КМ намного более ресурсозатратен? Я не понимаю, почему это так. - person jnm2; 30.03.2016
comment
@ jnm2: просто профилируйте два подхода в Mgmt Studio - подход KM имеет ориентировочную стоимость поддерева 1,106, а мой - 0,015 - примерно в 100 раз меньше ..... - person marc_s; 30.03.2016
comment
Я получаю примерно в 15,6 раза меньше, но он теряется до незначительности всякий раз, когда он используется в реальном запросе. Однако мне интересно, есть ли более эффективный способ правильной обработки символов XML. Есть внутреннее соединение с дорогим XML Reader tvf. - person jnm2; 30.03.2016
comment
@ jnm2: да, я бы хотел, чтобы Microsoft прислушалась и наконец дала нам встроенную (и оптимизированную!) функцию, чтобы справиться с этим .... - person marc_s; 30.03.2016
comment
@marc_s Нашел более производительную версию, только на ~ 1.08x медленнее: for xml path(''), type).value('(./text())[1]', 'nvarchar(max)') - person jnm2; 30.03.2016
comment
@marc_s Я думаю, что на 1.08x медленнее оно того стоит ... Я не вижу ничего хорошего от людей, решивших, что они не будут хранить амперсанды или угловые скобки. - person jnm2; 30.03.2016
comment
@marc_s Строка ORDER BY Sort важна? - person userSteve; 06.04.2017
comment
@userSteve: нет, если вам не важен порядок сортировки - он у меня только что был, так как OP также имел это ORDER BY в своем вопросе - person marc_s; 06.04.2017

Ответ @AlexanderMP правильный, но вы также можете рассмотреть возможность обработки нулей с помощью coalesce:

declare @CodeNameString  nvarchar(max)
set @CodeNameString = null
SELECT @CodeNameString = Coalesce(@CodeNameString + ', ', '') + cast(CodeName as varchar) from AccountCodes  
select @CodeNameString
person James Wiseman    schedule 04.03.2011
comment
Да забыл про coalesce / isnull. Но не лучше ли было бы проверить CodeName вместо @CodeNameString? Таким образом можно потерять данные. Еще лучше, вместо coaleste, имело бы смысл использовать старый добрый Where CodeName is not null. - person AlexanderMP; 04.03.2011
comment
это определенно лучшее и самое элегантное решение, спасибо большое! - person Federico Caccia; 15.03.2020

Для SQL Server 2005 и более поздних версий используйте Coalesce для nulls, а я использую Cast or Convert, если есть numeric values -

declare @CodeNameString  nvarchar(max)
select  @CodeNameString = COALESCE(@CodeNameString + ',', '')  + Cast(CodeName as varchar) from AccountCodes  ORDER BY Sort
select  @CodeNameString
person Vishal    schedule 04.03.2011
comment
100 - это маловато, вам не кажется ;-) - person James Wiseman; 04.03.2011
comment
@ Джеймс: Хм .. Просто скопировал это из Вопроса ОП, обновил его сейчас ... спасибо ... кстати, черт побери, ты меня опередил .. - person Vishal; 04.03.2011

from msdn Не используйте переменную в операторе SELECT для объединения значений (то есть для вычисления агрегированных значений). Могут возникнуть непредвиденные результаты запроса. Это связано с тем, что не гарантируется, что все выражения в списке SELECT (включая присваивания) будут выполнены ровно один раз для каждой выходной строки.

Вышеупомянутое, похоже, говорит о том, что объединение, как это сделано выше, недействительно, поскольку назначение может быть выполнено больше раз, чем количество строк, возвращаемых выбором

person peter    schedule 25.11.2014

Вот еще один пример из реальной жизни, который отлично работает, по крайней мере, с выпуском 2008 года (и позже).

Это исходный запрос, который использует простой max() для получения хотя бы одного из значений:

SELECT option_name, Field_M3_name, max(Option_value) AS "Option value", max(Sorting) AS "Sorted"
FROM Value_list group by Option_name, Field_M3_name
ORDER BY option_name, Field_M3_name

Улучшенная версия, где главное улучшение состоит в том, что мы показываем все значения через запятую:

SELECT from1.keys, from1.option_name, from1.Field_M3_name,

 Stuff((SELECT DISTINCT ', ' + [Option_value] FROM Value_list from2
  WHERE COALESCE(from2.Option_name,'') + '|' + COALESCE(from2.Field_M3_name,'') = from1.keys FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'') AS "Option values",

 Stuff((SELECT DISTINCT ', ' + CAST([Sorting] AS VARCHAR) FROM Value_list from2
  WHERE COALESCE(from2.Option_name,'') + '|' + COALESCE(from2.Field_M3_name,'') = from1.keys FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'') AS "Sorting"

FROM ((SELECT DISTINCT COALESCE(Option_name,'') + '|' + COALESCE(Field_M3_name,'') AS keys, Option_name, Field_M3_name FROM Value_list)
-- WHERE
) from1
ORDER BY keys

Обратите внимание, что мы решили все возможные NULL проблемы, которые я мог придумать, а также исправили ошибку, которую мы получили для числовых значений (Сортировка полей).

person Timo Riikonen    schedule 11.07.2014

person    schedule
comment
Простой и элегантный ответ. Есть ли ограничения на использование этого метода? (Значения NULL обрабатываются вариацией Джеймса Висеманна в этом ответе) - person Pac0; 18.08.2017