Функция SQL group concat в SQL Server [дубликат]
этот вопрос уже есть ответ здесь:
Если есть таблица под названием employee
EmpID EmpName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
результат мне нужен в таком формате:
EmpID EmpName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
Q: эта запись находится в том же Employee
таблица. У меня почти нет опыт использования UDFs, хранимых процедур, мне нужно сделать это через запрос.Возможно ли это без использования UDFs, SP.
4 ответов
- ДЛЯ XML PATH фишка и статьи
- CLR определяется пользователем aggregate
- для sql server предыдущей версии 2005-временные таблицы
Пример #1
DECLARE @t TABLE (EmpId INT, EmpName VARCHAR(100))
INSERT @t VALUES
(1, 'Mary'),(1, 'John'),(1, 'Sam'),(2, 'Alaina'),(2, 'Edward')
SELECT distinct
EmpId,
(
SELECT EmpName+','
FROM @t t2
WHERE t2.EmpId = t1.EmpId
FOR XML PATH('')
) Concatenated
FROM @t t1
как снять последнюю запятую-это самостоятельно
совокупный код CLR c# для #2
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;
using System.IO;
namespace DatabaseAssembly
{
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,
IsInvariantToNulls = true,
IsInvariantToDuplicates = true,
IsInvariantToOrder = true,
MaxByteSize = -1)]
public struct StringJoin : IBinarySerialize
{
private Dictionary<string, string> AggregationList
{
get
{
if (_list == null)
_list = new Dictionary<string, string>();
return _list;
}
}
private Dictionary<string, string> _list;
public void Init()
{
}
public void Accumulate(SqlString Value)
{
if (!Value.IsNull)
AggregationList[Value.Value.ToLowerInvariant()] = Value.Value;
}
public void Merge(StringJoin Group)
{
foreach (var key in Group.AggregationList.Keys)
AggregationList[key] = Group.AggregationList[key];
}
public SqlChars Terminate()
{
var sb = new StringBuilder();
foreach (var value in AggregationList.Values)
sb.Append(value);
return new SqlChars(sb.ToString());
}
#region IBinarySerialize Members
public void Read(System.IO.BinaryReader r)
{
try
{
while (true)
AggregationList[r.ReadString()] = r.ReadString();
}
catch (EndOfStreamException)
{
}
}
public void Write(System.IO.BinaryWriter w)
{
foreach (var key in AggregationList.Keys)
{
w.Write(key);
w.Write(AggregationList[key]);
}
}
#endregion
}
}
выбранный ответ от @OlegDok может вернуть правильный результат. Но представление может быть ужасным. Этот тестовый сценарий проиллюстрирует это.
создание временной таблицы:
CREATE table #temp (EmpId INT, EmpName VARCHAR(100))
;WITH N(N)AS
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a,N b,N c,N d,N e,N f)
INSERT #temp
SELECT EmpId, EmpName FROM (values(1, 'Mary'),(1, 'John'),(1, 'Sam')) x(EmpId, EmpName)
CROSS APPLY
(SELECT top 2000 N FROM tally) y
UNION ALL
SELECT EmpId, EmpName FROM (values(2, 'Alaina'),(2, 'Edward')) x(EmpId, EmpName)
CROSS APPLY
(SELECT top 2000 N FROM tally) y
Это только 10.000 строк. Но много одинаковых empid в.
этот запрос в ответе Олега занял 64 секунды в моей базе данных.
SELECT distinct
EmpId,
(
SELECT EmpName+','
FROM #temp t2
WHERE t2.EmpId = t1.EmpId
FOR XML PATH('')
) Concatenated
FROM #temp t1
Distinct не является правильным способом очистки строк в этой ситуации. Чтобы избежать этого декартового соединения, уменьшите начальное количество идентификаторов перед присоединением.
это правильный способ обработки этого:
;WITH CTE as
(
SELECT distinct EmpId
FROM #temp
)
SELECT
EmpId,
STUFF((
SELECT ','+EmpName
FROM #temp t2
WHERE t2.EmpId = t1.EmpId
FOR XML PATH('')
), 1,1,'') Concatenated
FROM CTE t1
Это занимает менее 1 секунды
Я думаю, что нет GROUP_CONCAT
функция в MSSQL. Это статьи показывает различные способы конкактенирования значений строк.
объединение значений, когда количество элементов мало и известно заранее
SELECT CategoryId,
MAX( CASE seq WHEN 1 THEN ProductName ELSE '' END ) + ', ' +
MAX( CASE seq WHEN 2 THEN ProductName ELSE '' END ) + ', ' +
MAX( CASE seq WHEN 3 THEN ProductName ELSE '' END ) + ', ' +
MAX( CASE seq WHEN 4 THEN ProductName ELSE '' END )
FROM ( SELECT p1.CategoryId, p1.ProductName,
( SELECT COUNT(*)
FROM Northwind.dbo.Products p2
WHERE p2.CategoryId = p1.CategoryId
AND p2.ProductName <= p1.ProductName )
FROM Northwind.dbo.Products p1 ) D ( CategoryId, ProductName, seq )
GROUP BY CategoryId ;
Это решение для примера, приведенного в начале:
SELECT DISTINCT emp_name,
STUFF(
(SELECT ', ' + RTRIM(proj_id)
FROM project_members AS t1
WHERE t1.emp_name = t2.emp_name
FOR XML PATH (''))
, 1, 1, '')
FROM project_members t2