Якщо вам дозволено використовувати CLR у вашому оточенні, це індивідуальний випадок для визначеного користувачем сукупності.
Зокрема, це, мабуть, шлях, якщо вихідні дані нетривіально великі та / або вам потрібно багато чого робити у вашому додатку. Я сильно підозрюю, що план запитів щодо рішення Аарона не буде масштабуватися, оскільки розмір вводу зростає. (Я спробував додати індекс до темп-таблиці, але це не допомогло.)
Це рішення, як і багато іншого, є компромісом:
- Політика / політика щодо навіть використання інтеграції CLR у вашому, або в клієнтовому середовищі.
- Функція CLR, швидше за все, швидше, і вона буде масштабуватися краще з урахуванням реального набору даних.
- Функція CLR буде повторно використана в інших запитах, і вам не доведеться дублювати (і налагоджувати) складний підзапит кожен раз, коли вам потрібно робити цей тип речей.
- Прямий T-SQL простіший, ніж написання та керування фрагментом зовнішнього коду.
- Можливо, ви не знаєте, як програмувати на C # або VB.
- тощо.
EDIT: Ну, я спробував перевірити, чи справді це краще, і виявляється, що вимогу, щоб коментарі були у визначеному порядку, наразі неможливо задовольнити за допомогою сукупної функції. :(
Див. 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