Я хочу вернуть результаты select Column from Table в строку, разделенную запятыми, с помощью SQL Server.
Рассматриваемый столбец довольно большой (nvarchar(2000)), поэтому решение должно обрабатывать очень большие значения результатов.





DECLARE @result nvarchar(max)
SET @result = ''
SELECT @result = @result + [Column] + N','
FROM [TABLE]
--TODO: trim last ',' if you require
PRINT @result
Если Column может быть нулевым, то либо сначала исключите его, либо используйте ISNULL / COALESCE - в противном случае один NULL нарушит всю последовательность. Эффективнее исключить его с помощью WHERE:
SELECT @result = @result + [Column] + N','
FROM [TABLE]
WHERE [Column] IS NOT NULL
SELECT @result = COALESCE (@result + N ',', N '') + [Column] позволит избежать последней запятой без штрафа за производительность.
без конечной запятой версии:
declare @s varchar(max);
select @s = isnull(@s + ', ' + lastname, lastname)
from person
order by lastname;
print @s;
Я ожидал, что это потребует еще несколько циклов процессора, хотя ... лично, если бы мне нужно было его обрезать, я бы вместо этого использовал строковые функции.
Строки MSSQL неизменяемы, я бы ожидал, что копирование больших строк (обрезка также даст копию) также потребует некоторых циклов процессора, хотя я не уверен. Я думаю, что если это разовая вещь, то микрооптимизации не нужны.
Я бы использовал coalesce вместо isnull
Я создал процедуру, которая будет динамически создавать CSV из любой произвольной таблицы без необходимости явно указывать столбцы. Это полезно, если вы не хотите писать собственный код каждый раз, когда хотите преобразовать таблицу SQL в строку CSV.
-- Description: Turns a query into a formatted CSV.
-- Any ORDER BY clause needs to be passed in the separate ORDER BY parameter.
CREATE PROC [dbo].[spQueryToCsv]
(
@query nvarchar(MAX), --A query to turn into CSV format. It should not include an ORDER BY clause.
@orderBy nvarchar(MAX) = NULL, --An optional ORDER BY clause. It should contain the words 'ORDER BY'.
@csv nvarchar(MAX) = NULL OUTPUT --The CSV output of the procedure.
)
AS
BEGIN
SET NOCOUNT ON;
IF @orderBy IS NULL BEGIN
SET @orderBy = '';
END
SET @orderBy = REPLACE(@orderBy, '''', '''''');
DECLARE @realQuery nvarchar(MAX) = '
DECLARE @headerRow nvarchar(MAX);
DECLARE @cols nvarchar(MAX);
SELECT * INTO #dynSql FROM (' + @query + ') sub;
SELECT @cols = ISNULL(@cols + '' + '''','''' + '', '''') + ''''''"'''' + ISNULL(REPLACE(CAST(['' + name + ''] AS nvarchar(max)), ''''"'''', ''''""''''), '''''''') + ''''"''''''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @cols = ''
SET @csv = (SELECT '' + @cols + '' FROM #dynSql ' + @orderBy + ' FOR XML PATH(''''m_m''''));
''
EXEC sys.sp_executesql @cols, N''@csv nvarchar(MAX) OUTPUT'', @csv=@csv OUTPUT
SELECT @headerRow = ISNULL(@headerRow + '','', '''') + ''"'' + REPLACE(name, ''"'', ''""'') + ''"''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @headerRow = @headerRow + CHAR(13) + CHAR(10);
SET @csv = @headerRow + @csv;
';
EXEC sys.sp_executesql @realQuery, N'@csv nvarchar(MAX) OUTPUT', @csv=@csv OUTPUT
SET @csv = REPLACE(REPLACE(@csv, '<m_m>', ''), '</m_m>', CHAR(13) + CHAR(10))
END
GO
Применение:
DECLARE @csv nvarchar(max)
EXEC [dbo].[spQueryToCsv] @query = 'SELECT * FROM Customers', @csv = @csv OUTPUT, @orderBy = 'ORDER BY CustomerId'
SELECT @csv
Это основано на аналогичном коде, который я написал, чтобы превратить произвольную таблицу в HTML-строка.
У меня возникли проблемы с методом, описанным в предложенном ответе. Я взял код из SQLAuthority. Но это работает каждый раз, если у вас есть проблема с предложенным решением.
SELECT SUBSTRING(
(SELECT ',' + s.ColumnName
FROM dbo.table s
ORDER BY s.ColumnName
FOR XML PATH('')),2,200000) AS CSV
GO
Для большого количества значений (~ 100k) этот ответ работает намного быстрее, чем принятый ответ, хотя 200000 необходимо увеличить.
Я бы предложил использовать STUFF (@ String, 1,1, '') вместо подстроки с огромным значением длины, если вы действительно не хотите установить максимальную длину.
STRING_AGG был добавлен в sql 2017
https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017
SELECT STRING_AGG (ColumnName, ',') AS csv
FROM TableName
GROUP BY ColumnName2
Для победы! Спасибо.
Если вы застряли в старых базах данных и не можете использовать STRING_AGG и не хотите обрезать после создания строки:
Опция 1:
DECLARE @Comma varchar(10) = '';
DECLARE @Result varchar(MAX) = '';
SELECT @Result = @Result + @Comma + Column, @Comma = ','
FROM Table;
Вариант 2:
DECLARE @Result varchar(MAX) = '';
SELECT @Result = @Result + CASE WHEN LEN(@Result) > 0 THEN ',' ELSE '' END + Column
FROM Table;
Вариант 1 мне нравится, потому что он короче. Я не проводил никаких тестов производительности, поэтому не могу сказать, что лучше в этом отношении.
Если вы выберете вариант 1, убедитесь, что @Comma установлена после @Result, иначе вы получите дополнительную запятую в начале строки.
Протестировано на SQL Server 2008
работает как рекламируется. Благодарю. для тех, кто найдет это позже, последнюю запятую легко скинуть:
select @result = substring(@result, 1, (LEN(@result)-1))