====== String concatenation as an aggregate operator ====== It is sometimes useful to concatenate together a set of related string values; e.g. to concatenate into a comma-separated list: * a list of column names for a single table; * a list of tags for an article. ===== SQL Server 2016 and earlier ===== Intuitively, we would like to be able to do something like this: SELECT object_id , COUNT(*) AS ColumnCount , StrConcat(', ' + name) AS ColumnNames FROM sys.columns GROUP BY object_id but have the problem that the ''StrConcat'' function doesn't exist! ==== Workaround ==== A workaround is to use the ''[[http://technet.microsoft.com/en-us/library/ms178107(v=sql.110).aspx|FOR XML]]'' clause with ''[[http://technet.microsoft.com/en-us/library/ms189885.aspx|PATH]]'' mode: SELECT t.object_id , COUNT(*) AS ColumnCount , ( SELECT ', ' + name FROM sys.columns sub WHERE t.object_id = sub.object_id ORDER BY sub.column_id FOR XML PATH(''), TYPE ).value('.','VARCHAR(MAX)') AS ColumnList FROM sys.columns t GROUP BY t.object_id You'll want to do something to remove the leading separator (in this case a comma) from the concatenated list -- I haven't done so here to avoid muddying the core query structure. Wrapping it in ''STUFF(////, 1, 2, '''') does the trick. ==== Boilerplate ==== Generally, this query has the form: SELECT t.GroupingField , ( SELECT ', ' + FieldToConcatenate FROM TableName sub WHERE t.GroupingField = sub.GroupingField ORDER BY sub.FieldToOrderBy FOR XML PATH(''), TYPE ).value('.','VARCHAR(MAX)') AS AggregateValue FROM TableName t GROUP BY t.GroupingField ===== SQL Server 2017 and beyond ===== This got a whole lot easier! SELECT t.object_id , COUNT(*) AS ColumnCount , STRING_AGG([name], ', ') AS ColumnList FROM sys.columns t GROUP BY t.object_id {{tag> tsql aggregate}}