Jika Anda diizinkan menggunakan CLR di lingkungan Anda, ini adalah kasus yang dibuat khusus untuk agregat yang ditentukan pengguna.
Secara khusus, ini mungkin cara untuk pergi jika sumber data non-sepele besar dan / atau Anda perlu melakukan hal semacam ini banyak dalam aplikasi Anda. Saya sangat curiga rencana permintaan untuk solusi Aaron tidak akan menskala dengan baik karena ukuran input bertambah. (Saya mencoba menambahkan indeks ke tabel temp, tetapi itu tidak membantu.)
Solusi ini, seperti banyak hal lainnya, merupakan kompromi:
- Politik / kebijakan untuk bahkan menggunakan Integrasi CLR di lingkungan Anda, atau klien Anda.
- Fungsi CLR kemungkinan lebih cepat, dan akan menskala dengan lebih baik mengingat sekumpulan data nyata.
- Fungsi CLR akan dapat digunakan kembali dalam permintaan lain, dan Anda tidak perlu menduplikasi (dan men-debug) subquery kompleks setiap kali Anda perlu melakukan hal semacam ini.
- Straight T-SQL lebih sederhana daripada menulis dan mengelola sepotong kode eksternal.
- Mungkin Anda tidak tahu cara memprogram dalam C # atau VB.
- dll.
EDIT: Ya, saya pergi untuk mencoba melihat apakah ini sebenarnya lebih baik, dan ternyata persyaratan bahwa komentar dalam urutan tertentu saat ini tidak mungkin dipenuhi dengan menggunakan fungsi agregat. :(
Lihat SqlUserDefinedAggregateAttribute.IsInvariantToOrder . Pada dasarnya, apa yang perlu Anda lakukan adalah OVER(PARTITION BY customer_code ORDER BY row_num)
tetapi ORDER BY
tidak didukung dalam OVER
klausa ketika menjumlahkan. Saya berasumsi menambahkan fungsi ini ke SQL Server membuka sekaleng cacing, karena apa yang perlu diubah dalam rencana eksekusi itu sepele. Tautan yang disebutkan di atas mengatakan ini dicadangkan untuk penggunaan di masa mendatang, jadi ini dapat diterapkan di masa mendatang (pada 2005 Anda mungkin kurang beruntung).
Ini bisa masih bisa dicapai dengan kemasan dan parsing row_num
nilai ke string dikumpulkan, dan kemudian melakukan semacam dalam objek CLR ... yang tampaknya cukup hackish.
Dalam hal apa pun, di bawah ini adalah kode yang saya gunakan seandainya ada orang lain yang menganggap ini berguna walaupun dengan batasan. Saya akan meninggalkan bagian peretasan sebagai latihan untuk pembaca. Perhatikan bahwa saya menggunakan AdventureWorks (2005) untuk data uji.
Perakitan agregat:
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 untuk pengujian ( CREATE ASSEMBLY
, dan sp_configure
untuk mengaktifkan CLR dihilangkan):
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