Question: Je veux écrire une fonction d'agrégation personnalisée qui concatène la chaîne par groupe.
Pour que je puisse faire
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD1, FIELD2
Tout ce que je trouve, ce sont des fonctions d'agrégation SQL CRL, mais j'ai besoin de SQL, sans CLR.
Modifier: 1
La requête devrait ressembler à ceci:
SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2
FROM TABLE_XY
GROUP BY FIELD0
Éditer 2:
Il est vrai que ce n'est pas possible sans CLR.
Cependant, la réponse de sous-sélection par astander peut être modifiée pour ne pas coder en XML les caractères spéciaux.
Le changement subtil pour cela est d'ajouter ceci après "POUR LE CHEMIN XML":,
TYPE
).value('.[1]', 'nvarchar(MAX)')
Voici quelques exemples
DECLARE @tT table([A] varchar(200), [B] varchar(200));
INSERT INTO @tT VALUES ('T_A', 'C_A');
INSERT INTO @tT VALUES ('T_A', 'C_B');
INSERT INTO @tT VALUES ('T_B', 'C_A');
INSERT INTO @tT VALUES ('T_C', 'C_A');
INSERT INTO @tT VALUES ('T_C', 'C_B');
INSERT INTO @tT VALUES ('T_C', 'C_C');
SELECT
A AS [A]
,
(
STUFF
(
(
SELECT DISTINCT
', ' + tempT.B AS wtf
FROM @tT AS tempT
WHERE (1=1)
--AND tempT.TT_Status = 1
AND tempT.A = myT.A
ORDER BY wtf
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 2, ''
)
) AS [B]
FROM @tT AS myT
GROUP BY A
SELECT
(
SELECT
',äöü<>' + RM_NR AS [text()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
) AS XmlEncodedNoNothing
,
SUBSTRING
(
(
SELECT
',äöü<>' + RM_NR AS [data()]
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH('')
)
,2
,10000
) AS XmlEncodedSubstring
,
(
STUFF
(
(
SELECT ',äöü<>' + RM_NR + CHAR(10)
FROM T_Room
WHERE RM_Status = 1
ORDER BY RM_NR
FOR XML PATH, TYPE
).value('.[1]', 'nvarchar(MAX)')
, 1, 1, ''
)
) AS XmlDecodedStuffInsteadSubstring
Vous ne pouvez pas écrire d'agrégats personnalisés en dehors du CLR.
Les seuls types de fonctions que vous pouvez écrire en T-SQL pur sont les fonctions scalaires et table.
Comparez les pages pour CREATE AGGREGATE , qui répertorie uniquement les options de style CLR, avec CREATE FUNCTION , qui affiche les options T-SQL et CLR.
Jetez un oeil à quelque chose comme. Ce n'est pas une fonction agrégée. Si vous souhaitez implémenter votre propre fonction d'agrégation, elle devra être CLR ...
DECLARE @Table TABLE(
ID INT,
Val VARCHAR(50)
)
INSERT INTO @Table (ID,Val) SELECT 1, 'A'
INSERT INTO @Table (ID,Val) SELECT 1, 'B'
INSERT INTO @Table (ID,Val) SELECT 1, 'C'
INSERT INTO @Table (ID,Val) SELECT 2, 'B'
INSERT INTO @Table (ID,Val) SELECT 2, 'C'
--Concat
SELECT t.ID,
SUM(t.ID),
stuff(
(
select ',' + t1.Val
from @Table t1
where t1.ID = t.ID
order by t1.Val
for xml path('')
),1,1,'') Concats
FROM @Table t
GROUP BY t.ID
À partir de 2017, il existe une fonction d'agrégation concaténée intégrée STRING_AGG :)
https://docs.Microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017
Cette solution fonctionne sans besoin de déploiement à partir de Visual studio ou d'un fichier dll sur le serveur.
Copiez-collez et ça marche!
http://groupconcat.codeplex.com/
dbo.GROUP_CONCAT(VALUE )
dbo.GROUP_CONCAT_D(VALUE ), DELIMITER )
dbo.GROUP_CONCAT_DS(VALUE , DELIMITER , SORT_ORDER )
dbo.GROUP_CONCAT_S(VALUE , SORT_ORDER )
Trouvé ceci lien autour de la concaténation qui couvre des méthodes comme
Concaténation de valeurs lorsque le nombre d'éléments n'est pas connu
Approches non fiables
Bien qu'il ne couvre pas les fonctions d'agression, il peut y avoir une utilisation autour de la concaténation pour vous aider avec votre problème.
Vous pouvez faire quelque chose comme ce que j'ai fait ci-dessous pour créer une fonction de concaténation d'agrégat personnalisée en T-SQL pur. Évidemment, je suis allé avec un nom de table codé en dur et un groupe par colonne, mais cela devrait illustrer l'approche. Il existe probablement un moyen d'en faire une fonction vraiment générique en utilisant TSQL dynamique construit à partir de paramètres d'entrée.
/*
User defined function to help perform concatenations as an aggregate function
Based on AdventureWorks2008R2 SalesOrderDetail table
*/
--select * from sales.SalesOrderDetail
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = N'fnConcatenate')
DROP FUNCTION fnConcatenate
GO
CREATE FUNCTION fnConcatenate
(
@GroupByValue int
)
returnS varchar(8000)
as
BEGIN
DECLARE @SqlString varchar(8000)
Declare @TempStore varchar(25)
select @SqlString =''
Declare @MyCursor as Cursor
SET @MyCursor = CURSOR FAST_FORWARD
FOR
Select ProductID
From sales.SalesOrderDetail where SalesOrderID = @GroupByValue
order by SalesOrderDetailID asc
OPEN @MyCursor
FETCH NEXT FROM @MyCursor
INTO @TempStore
WHILE @@FETCH_STATUS = 0
BEGIN
select @SqlString = ltrim(rtrim(@TempStore )) +',' + ltrim(rtrim(@SqlString))
FETCH NEXT FROM @MyCursor INTO @TempStore
END
CLOSE @MyCursor
DEALLOCATE @MyCursor
RETURN @SqlString
END
GO
select SalesOrderID, Sum(OrderQty), COUNT(*) as DetailCount , dbo.fnConcatenate(salesOrderID) as ConCatenatedProductList
from sales.SalesOrderDetail
where salesOrderID= 56805
group by SalesOrderID