Ответ 1
В MSSQL нет встроенного способа сделать это.
Имитация функции group_concat MySQL в Microsoft SQL Server 2005? содержит хорошее описание того, как реализовать обходной путь.
У меня есть таблица с только идентификатором продукта и идентификатором категории (продукты могут быть более чем в одной категории). Как я могу сгладить идентификатор категории в столбце продукта, поэтому я заканчиваю это следующим образом:
id | name | desc | categories
1 | test1 | lorem | 1,3,4,23
2 | test2 | ipsom | 4,6,24
Это похоже на то, что мне нужно зациклиться на отдельной таблице для столбца категорий. Как я могу это сделать или есть лучший способ?
В MSSQL нет встроенного способа сделать это.
Имитация функции group_concat MySQL в Microsoft SQL Server 2005? содержит хорошее описание того, как реализовать обходной путь.
Я создал функцию агрегации CLR, которая принимает столбец varchar
и возвращает все значения, разделенные запятыми. Другими словами, он объединяет несколько строк в список, разделенный запятыми. Я уверен, что его производительность намного лучше, чем любой трюк T-Sql.
Как любая совокупная функция, ее можно использовать в сочетании с group by
. Например:
SELECT id, name, desc, JoinStrings(CONVERT(VARCHAR(20), category_id))
FROM product p
INNER JOIN category_products c ON p.category_id = c.category_id
GROUP BY id, name, desc
Здесь код С# для создания сборки CLR в Sql Server 2008:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates=false, IsInvariantToOrder=false, IsInvariantToNulls=true, MaxByteSize=-1)]
public struct JoinStrings : IBinarySerialize
{
private char[] sb;
private int pos;
public void Init()
{
sb = new char[512000];
pos = 0;
}
public void Accumulate(SqlString Value)
{
if (Value.IsNull) return;
char[] src = Value.ToString().ToCharArray();
Array.Copy(src, 0, sb, pos, src.Length);
pos += src.Length;
sb[pos] = ',';
pos++;
}
public void Merge(JoinStrings Group)
{
Accumulate(Group.Terminate());
}
public SqlString Terminate()
{
if (pos <= 0)
return new SqlString();
else
return new SqlString(new String(sb, 0, pos-1));
}
public void Read(System.IO.BinaryReader r)
{
this.Init();
pos = r.ReadInt32();
r.Read(sb, 0, pos);
}
public void Write(System.IO.BinaryWriter w)
{
w.Write(pos);
w.Write(sb, 0, pos);
}
}
Здесь код для создания функции (хотя развертывание из Visual Studio должно выполняться автоматически):
CREATE AGGREGATE [dbo].[JoinStrings]
(@s [nvarchar](4000))
RETURNS[nvarchar](max)
EXTERNAL NAME [YouAssemblyName].[JoinStrings]
Я бы предложил использовать рекурсивный CTE. Я считаю, что это будет примерно так:
select productid, categoryid,
row_number() over (partition by id order by categoryid) as rownum
into #tabletorecurse
from TABLENAME
with finaloutput as
(
select productid as id, name, desc, categoryid as categories, rownum
from #tabletorecurse
join PRODUCTTABLE
on PRODUCTTABLE.id = #tabletorecurse.productid
where rownum = 1
union all
select tr.id, tr.name, tr.desc,
finaloutput.categories + ', ' + tr.categoryid, tr.rownum
from #tabletorecurse as tr
join finaloutput
on finaloutput.rownum + 1 = tr.rownum
and finaloutput.id = tr.productid
)
select id, name, desc, categories
from finaloutput
join
(
select max(rownum) as maxrow, id
from finaloutput
group by id
) as maxvalues
on maxvalues.id = finaloutput.id
and maxvalues.maxrow = finaloutput.rownum
Используйте функцию.
Это делает поиск текста, поэтому вам нужно будет адаптироваться.
COALESCE - это просто, чтобы положить,.
Это из крупномасштабного производственного приложения - оно работает и быстро.
Функция была спрошена JustinPony, поскольку функция медленная
Я нажимаю несколько таблиц из миллиона записей, но возвращаю только 100 строк.
Функция применяется только к сотням строк.
использование:
select top 5 sID, ( select [dbo].[JoinMVEnum](docSVsys.sID, '140') ) as [Flag Issue]
from docSVsys
функция
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[JoinMVText]
(
@sID int,
@fieldID tinyint
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @MVtextList varchar(max)
SELECT @MVtextList = COALESCE(@MVtextList + '; ', '') + docMVtext.value
FROM docMVtext with (nolock)
WHERE docMVtext.sID = @sID and fieldID = @fieldID
RETURN @MVtextList
END
GO