Wenn Sie CLR in Ihrer Umgebung verwenden dürfen, ist dies ein maßgeschneiderter Fall für ein benutzerdefiniertes Aggregat.
Dies ist wahrscheinlich der richtige Weg, wenn die Quelldaten nicht trivial groß sind und / oder Sie in Ihrer Anwendung häufig solche Aufgaben ausführen müssen. Ich vermute sehr, dass der Abfrageplan für Aarons Lösung nicht gut skaliert wird, wenn die Eingabegröße zunimmt. (Ich habe versucht, der temporären Tabelle einen Index hinzuzufügen, aber das hat nicht geholfen.)
Diese Lösung ist, wie viele andere Dinge, ein Kompromiss:
- Politik / Richtlinie für die Verwendung der CLR-Integration in Ihrer oder der Umgebung Ihres Kunden.
- Die CLR-Funktion ist wahrscheinlich schneller und skaliert bei einem realen Datensatz besser.
- Die CLR-Funktion kann in anderen Abfragen wiederverwendet werden, und Sie müssen eine komplexe Unterabfrage nicht jedes Mal duplizieren (und debuggen), wenn Sie dies tun müssen.
- Straight T-SQL ist einfacher als das Schreiben und Verwalten von externem Code.
- Vielleicht wissen Sie nicht, wie man in C # oder VB programmiert.
- etc.
EDIT: Nun, ich habe versucht herauszufinden, ob dies tatsächlich besser ist, und es stellt sich heraus, dass die Anforderungen, dass die Kommentare in einer bestimmten Reihenfolge vorliegen, derzeit mit einer Aggregatfunktion nicht erfüllt werden können. :(
Siehe SqlUserDefinedAggregateAttribute.IsInvariantToOrder . Im Grunde, was Sie tun müssen , ist OVER(PARTITION BY customer_code ORDER BY row_num)
aber ORDER BY
nicht in der unterstützte OVER
Klausel , wenn die Aggregation. Ich gehe davon aus, dass das Hinzufügen dieser Funktionalität zu SQL Server eine Dose Würmer öffnet, da es trivial ist, was im Ausführungsplan geändert werden müsste. Der oben genannte Link besagt, dass dies für die zukünftige Verwendung reserviert ist, so dass dies in Zukunft implementiert werden könnte (2005 haben Sie jedoch wahrscheinlich kein Glück).
Dies könnte immer noch erreicht werden, indem der row_num
Wert in die aggregierte Zeichenfolge gepackt und analysiert wird und dann die Sortierung innerhalb des CLR-Objekts durchgeführt wird ... was ziemlich hackisch erscheint.
In jedem Fall ist unten der Code aufgeführt, den ich verwendet habe, falls jemand dies trotz der Einschränkung für nützlich hält. Ich lasse den Hacking-Teil als Übung für den Leser. Beachten Sie, dass ich AdventureWorks (2005) für Testdaten verwendet habe.
Aggregatmontage:
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 zum Testen ( CREATE ASSEMBLY
und sp_configure
zum Aktivieren von CLR weggelassen):
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