web-dev-qa-db-fra.com

Fonction d'agrégation personnalisée (concat) dans SQL Server

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   
26
Stefan Steiger

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.

13

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
13
Adriaan Stander

À 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

5

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 )
3
Xilmiki

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

  • Méthode CTE récursive
  • Les méthodes XML Blackbox
  • Utilisation du Common Language Runtime
  • UDF scalaire avec récursivité
  • Table UDF de valeur avec une boucle WHILE
  • SQL dynamique
  • L'approche Cursor

Approches non fiables

  • UDF scalaire avec l'extension de mise à jour t-SQL
  • UDF scalaire avec concaténation variable dans SELECT

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.

3
kevchadders

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 
0
Bijimon