Doublons possibles:
fonction de type Implode dans SQL Server 2000?
Concaténer les valeurs de ligne T-SQL
J'ai une vue que j'interroge qui ressemble à ceci:
BuildingName PollNumber ------------ ---------- Foo Centre 12 Foo Centre 13 Foo Centre 14 Bar Hall 15 Bar Hall 16 Baz School 17
J'ai besoin d'écrire une requête qui regroupe BuildingNames et affiche une liste de PollNumbers comme ceci:
BuildingName PollNumbers ------------ ----------- Foo Centre 12, 13, 14 Bar Hall 15, 16 Baz School 17
Comment puis-je faire cela dans T-SQL? Je préférerais ne pas recourir à une procédure stockée pour cela, car cela peut sembler excessif, mais je ne suis pas exactement un utilisateur de base de données. Il me semble que j'ai besoin d'une fonction d'agrégation telle que SUM () ou AVG (), mais je ne sais pas si T-SQL en a une. J'utilise SQL Server 2005.
pour SQL Server 2017 et versions ultérieures:
STRING_AGG ()
set nocount on;
declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))
insert into @YourTable VALUES (1,1,'CCC')
insert into @YourTable VALUES (2,2,'B<&>B')
insert into @YourTable VALUES (3,2,'AAA')
insert into @YourTable VALUES (4,3,'<br>')
insert into @YourTable VALUES (5,3,'A & Z')
set nocount off
SELECT
t1.HeaderValue
,STUFF(
(SELECT
', ' + t2.ChildValue
FROM @YourTable t2
WHERE t1.HeaderValue=t2.HeaderValue
ORDER BY t2.ChildValue
FOR XML PATH(''), TYPE
).value('.','varchar(max)')
,1,2, ''
) AS ChildValues
FROM @YourTable t1
GROUP BY t1.HeaderValue
SELECT
HeaderValue, STRING_AGG(ChildValue,', ')
FROM @YourTable
GROUP BY HeaderValue
SORTIE:
HeaderValue
----------- -------------
1 CCC
2 B<&>B, AAA
3 <br>, A & Z
(3 rows affected)
pour SQL Server 2005 et jusqu'en 2016, vous devez faire quelque chose comme ceci:
--Concatenation with FOR XML and eleminating control/encoded character expansion "& < >"
set nocount on;
declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))
insert into @YourTable VALUES (1,1,'CCC')
insert into @YourTable VALUES (2,2,'B<&>B')
insert into @YourTable VALUES (3,2,'AAA')
insert into @YourTable VALUES (4,3,'<br>')
insert into @YourTable VALUES (5,3,'A & Z')
set nocount off
SELECT
t1.HeaderValue
,STUFF(
(SELECT
', ' + t2.ChildValue
FROM @YourTable t2
WHERE t1.HeaderValue=t2.HeaderValue
ORDER BY t2.ChildValue
FOR XML PATH(''), TYPE
).value('.','varchar(max)')
,1,2, ''
) AS ChildValues
FROM @YourTable t1
GROUP BY t1.HeaderValue
SORTIE:
HeaderValue ChildValues
----------- -------------------
1 CCC
2 AAA, B<&>B
3 <br>, A & Z
(3 row(s) affected)
Aussi, faites attention, pas tous FOR XML PATH
_ les concaténations gèrent correctement les caractères spéciaux XML, comme le montre l'exemple ci-dessus.
Il n'y a pas de fonction intégrée dans Sql Server, mais cela peut être obtenu en écrivant un agrégat défini par l'utilisateur. Cet article mentionne une telle fonction dans les exemples SQL Server: http://msdn.Microsoft.com/en-us/library/ms182741.aspx
A titre d'exemple, j'inclus le code d'un agrégat Concatenate. Pour l'utiliser, créez un projet de base de données dans Visual Studio, ajoutez le nouveau SqlAggregate et remplacez le code par l'exemple ci-dessous. Une fois déployé, vous devriez trouver un nouvel assemblage dans votre base de données et une fonction d'agrégat Concatenate
using System;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Server;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000, Name = "Concatenate")]
public class Concatenate : IBinarySerialize
{
private StringBuilder _intermediateResult;
internal string IntermediateResult {
get
{
return _intermediateResult.ToString();
}
}
public void Init()
{
_intermediateResult = new StringBuilder();
}
public void Accumulate(SqlString value)
{
if (value.IsNull) return;
_intermediateResult.Append(value.Value);
}
public void Merge(Concatenate other)
{
if (null == other)
return;
_intermediateResult.Append(other._intermediateResult);
}
public SqlString Terminate()
{
var output = string.Empty;
if (_intermediateResult != null && _intermediateResult.Length > 0)
output = _intermediateResult.ToString(0, _intermediateResult.Length - 1);
return new SqlString(output);
}
public void Read(BinaryReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
_intermediateResult = new StringBuilder(reader.ReadString());
}
public void Write(BinaryWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
writer.Write(_intermediateResult.ToString());
}
}
Pour l'utiliser, vous pouvez simplement écrire une requête agrégée:
create table test(
id int identity(1,1) not null
primary key
, class tinyint not null
, name nvarchar(120) not null )
insert into test values
(1, N'This'),
(1, N'is'),
(1, N'just'),
(1, N'a'),
(1, N'test'),
(2, N','),
(3, N'do'),
(3, N'not'),
(3, N'be'),
(3, N'alarmed'),
(3, N','),
(3, N'this'),
(3, N'is'),
(3, N'just'),
(3, N'a'),
(3, N'test')
select dbo.Concatenate(name + ' ')
from test
group by class
drop table test
Le résultat de la requête est:
-- Output
-- ===================
-- This is just a test
-- ,
-- do not be alarmed , this is just a test
J'ai emballé la classe et l'ensemble en tant que script que vous pouvez trouver ici: https://Gist.github.com/FilipDeVos/5b7b4addea1812067b09