Если вам разрешено использовать CLR в вашей среде, это индивидуальный подход для пользовательского агрегата.
В частности, это, вероятно, тот путь, если исходные данные нетривиально велики, и / или вам нужно много чего делать в своем приложении. Я сильно подозреваю, что план запроса для решения Аарона не будет хорошо масштабироваться по мере увеличения размера ввода. (Я попытался добавить индекс во временную таблицу, но это не помогло.)
Это решение, как и многие другие, является компромиссом:
- Политика / политика даже для использования CLR Integration в вашей среде или среде вашего клиента.
- Функция CLR, скорее всего, быстрее и будет лучше масштабироваться с учетом реального набора данных.
- Функция CLR будет многократно использоваться в других запросах, и вам не придется дублировать (и отлаживать) сложный подзапрос каждый раз, когда вам понадобится выполнить подобные действия.
- Прямой T-SQL проще, чем написание и управление фрагментом внешнего кода.
- Возможно, вы не знаете, как программировать на C # или VB.
- и т.п.
РЕДАКТИРОВАТЬ: Ну, я пошел, чтобы попытаться увидеть, было ли это на самом деле лучше, и оказалось, что требование, чтобы комментарии были в определенном порядке, в настоящее время невозможно удовлетворить с помощью функции агрегирования. :(
См. SqlUserDefinedAggregateAttribute.IsInvariantToOrder . По сути, вам нужно, OVER(PARTITION BY customer_code ORDER BY row_num)
но ORDER BY
не поддерживается в OVER
предложении при агрегировании. Я предполагаю, что добавление этой функции в SQL Server открывает червя, потому что то, что нужно изменить в плане выполнения, тривиально. Вышеупомянутая ссылка говорит, что это зарезервировано для будущего использования, так что это может быть реализовано в будущем (хотя в 2005 году вам, вероятно, не повезло).
Это может еще быть достигнуто путем упаковки и разбора row_num
значения в агрегированной строку, а затем делает вид внутри объекта CLR ... который кажется довольно хаком.
В любом случае, ниже приведен код, который я использовал на тот случай, если кто-то сочтет это полезным, даже с ограничениями. Я оставлю хакерскую часть как упражнение для читателя. Обратите внимание, что я использовал AdventureWorks (2005) для тестовых данных.
Агрегатная сборка:
using System;
using System.IO;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace MyCompany.SqlServer
{
[Serializable]
[SqlUserDefinedAggregate
(
Format.UserDefined,
IsNullIfEmpty = false,
IsInvariantToDuplicates = false,
IsInvariantToNulls = true,
IsInvariantToOrder = false,
MaxByteSize = -1
)]
public class StringConcatAggregate : IBinarySerialize
{
private string _accum;
private bool _isEmpty;
public void Init()
{
_accum = string.Empty;
_isEmpty = true;
}
public void Accumulate(SqlString value)
{
if (!value.IsNull)
{
if (!_isEmpty)
_accum += ' ';
else
_isEmpty = false;
_accum += value.Value;
}
}
public void Merge(StringConcatAggregate value)
{
Accumulate(value.Terminate());
}
public SqlString Terminate()
{
return new SqlString(_accum);
}
public void Read(BinaryReader r)
{
this.Init();
_accum = r.ReadString();
_isEmpty = _accum.Length == 0;
}
public void Write(BinaryWriter w)
{
w.Write(_accum);
}
}
}
T-SQL для тестирования ( CREATE ASSEMBLY
и sp_configure
для включения CLR опущено):
CREATE TABLE [dbo].[Comments]
(
CustomerCode int NOT NULL,
RowNum int NOT NULL,
Comments nvarchar(25) NOT NULL
)
INSERT INTO [dbo].[Comments](CustomerCode, RowNum, Comments)
SELECT
DENSE_RANK() OVER(ORDER BY FirstName),
ROW_NUMBER() OVER(PARTITION BY FirstName ORDER BY ContactID),
Phone
FROM [AdventureWorks].[Person].[Contact]
GO
CREATE AGGREGATE [dbo].[StringConcatAggregate]
(
@input nvarchar(MAX)
)
RETURNS nvarchar(MAX)
EXTERNAL NAME StringConcatAggregate.[MyCompany.SqlServer.StringConcatAggregate]
GO
SELECT
CustomerCode,
[dbo].[StringConcatAggregate](Comments) AS AllComments
FROM [dbo].[Comments]
GROUP BY CustomerCode