Se ti è permesso usare CLR nel tuo ambiente, questo è un caso su misura per un aggregato definito dall'utente.
In particolare, questo è probabilmente il modo di procedere se i dati di origine sono di dimensioni non banali e / o se è necessario eseguire molto questo tipo di cose nella propria applicazione. Sospetto fortemente che il piano di query per la soluzione di Aaron non si ridimensioni bene con l'aumentare della dimensione dell'input. (Ho provato ad aggiungere un indice alla tabella temporanea, ma non è stato d'aiuto.)
Questa soluzione, come molte altre cose, è un compromesso:
- Politica / politica per l'uso uniforme dell'integrazione CLR nel proprio ambiente o client.
- La funzione CLR è probabilmente più veloce e si ridimensionerà meglio dato un insieme reale di dati.
- La funzione CLR sarà riutilizzabile in altre query e non sarà necessario duplicare (ed eseguire il debug) di una subquery complessa ogni volta che è necessario eseguire questo tipo di cose.
- Straight T-SQL è più semplice che scrivere e gestire un pezzo di codice esterno.
- Forse non sai come programmare in C # o VB.
- eccetera.
EDIT: Beh, sono andato a cercare di vedere se questo fosse effettivamente meglio, e risulta che il requisito per cui i commenti siano in un ordine specifico non è attualmente possibile soddisfare utilizzando una funzione aggregata. :(
Vedi SqlUserDefinedAggregateAttribute.IsInvariantToOrder . Fondamentalmente, ciò che devi fare è OVER(PARTITION BY customer_code ORDER BY row_num)
ma ORDER BY
non è supportato nella OVER
clausola durante l'aggregazione. Suppongo che l'aggiunta di questa funzionalità a SQL Server apra una lattina di worm, perché ciò che dovrebbe essere modificato nel piano di esecuzione è banale. Il link di cui sopra dice che è riservato per un uso futuro, quindi potrebbe essere implementato in futuro (nel 2005 probabilmente sei sfortunato).
Questo potrebbe ancora essere realizzato impacchettando e analizzando il row_num
valore nella stringa aggregata, e quindi facendo l'ordinamento all'interno dell'oggetto CLR ... che sembra piuttosto hacker.
In ogni caso, di seguito è riportato il codice che ho usato nel caso in cui qualcun altro lo ritenga utile anche con la limitazione. Lascerò la parte di hacking come esercizio per il lettore. Si noti che ho usato AdventureWorks (2005) per i dati di test.
Assemblaggio aggregato:
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 per il test ( CREATE ASSEMBLY
e sp_configure
per abilitare CLR omesso):
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