21

Pregunta: Deseo escribir una función de agregado personalizada que concatene una cadena en un grupo.Función de agregado personalizado (concat) en SQL Server

Así que yo puedo hacer un

SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2 
FROM TABLE_XY 
GROUP BY FIELD1, FIELD2 

Todo lo que encontramos es CRL funciones de agregado de SQL, pero necesito SQL, sin CLR.



Edición: 1
La consulta debería tener este aspecto:

SELECT SUM(FIELD1) as f1, MYCONCAT(FIELD2) as f2 
    FROM TABLE_XY 
    GROUP BY FIELD0 



Edición 2:
Es cierto que no es posible sin CLR.
Sin embargo, la respuesta de subselección de un espectador se puede modificar para que no codifique XML los caracteres especiales.

El cambio sutil para esto es agregar esto después "para la trayectoria de XML": ,

TYPE 
       ).value('.[1]', 'nvarchar(MAX)') 

Aquí algunos ejemplos

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 
+2

En el caso de su código de ejemplo sólo habrá un valor para CAMPO2 de todos modos (GROUP BY), de modo que no es necesario la función. Supongo que tu ejemplo es incorrecto. – sqlvogel

+0

Ahahaha, bien, maldita sea, tienes razón. Field0 sería un UID (group by), field1 y field2 no debería estar en la cláusula group ... –

Respuesta

11

No se puede escribir agregados personalizados fuera del CLR.

El único tipo de funciones que puede escribir en T-SQL puro son funciones escalares y con valores de tabla.

Compara las páginas para CREATE AGGREGATE, que solo enumera las opciones de estilo CLR, con CREATE FUNCTION, que muestra las opciones de T-SQL y CLR.

+0

El problema es que no puedo dar una función CLR a un cliente para la instalación, especialmente no en un área de seguridad restringida. .. Pero de todos modos, 'no es posible' responde mi pregunta. –

+2

@Quandary Solo una sugerencia: si le dice a un administrador de db que necesita habilitar clr en sql y cargar su ensamblaje, por supuesto, va a decir "Ah, eso es un riesgo de seguridad". Pero si tiene su aplicación, haga todo ese trabajo y solo diga "La aplicación necesita estos derechos" (o mejor aún, solo téngalo en un instalador para que solo el instalador necesite esos derechos), entonces es más probable que obtenga la aprobación. Quizás no sea una opción para ti en absoluto (y me doy cuenta de que esta es una pregunta de hace un año) pero pensé que simplemente lanzaría ese pensamiento por ahí. –

+0

@Brandon Moore: ;-) Sí, identificaste con éxito el problema entre las líneas, sugiriendo la política para el rescate. Sin embargo, dudo mucho que cualquier administrador de base de datos suficientemente inteligente dé a cualquier aplicación los derechos de alterar la configuración de toda la base de datos ... –

13

Eche un vistazo a algo así como. Esta no es una función agregada. Si desea implementar su propia función de agregado, tendrá que ser 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 
+1

La variable de la tabla se da como un ejemplo ... Reemplácela con la tabla que desee agregar. –

+0

@Quandary: Realmente no entiendo tu queja. La tabla temporal es solo para mostrar cómo funciona. No conozco la ruta xml, pero ¿por qué importa si es ilegible, poco intuitiva, etc. si obtienes una solución corta, simple y funcional para tu problema? –

+0

+1 @astander Cuanto más intento su solución, más me gusta. Funciona inmediatamente, no requiere preparación. Y hasta ahora no vi una solución CLR lista para usar. (Mi primera impresión fue que era lenta, pero que fue causada por algún artefacto) –

3

Encontrado este link alrededor de concatenación que abarca métodos como

Concatenación de valores cuando el número de artículos no son conocidos

  • Recursive CTE métodos método
  • El blackbox XML
  • Uso del Common Language Runtime
  • escalar UDF con recursión
  • valores de tabla UDF con un bucle while
  • SQL dinámico enfoque
  • el cursor

Los enfoques no fiables

  • escalar UDF con t- Extensión de actualización SQL
  • UDF escalar con variable conca tenation en SELECCIONAR

Aunque no cubre las funciones básicas, puede que haya algo de uso en la concatenación para ayudarlo con su problema.

+4

@Quandary: las 2 mejores maneras de realizar esta tarea ** son ** 'XML PATH' y' CLR AGGREGATES'. Si descarta ambas cosas, se quedará buscando las soluciones restantes mucho peores en la lista anterior. No hay otras formas. –

+0

@Martin Smith: Finalmente encontré la solución correcta al problema. Consulte edición 2. –

0

Puede hacer algo como lo que he hecho a continuación para crear una función de concatenación agregada personalizada en T-SQL puro. Obviamente, me he ido con un nombre de tabla codificado y un grupo por columna, pero debería ilustrar el enfoque. Probablemente exista alguna forma de hacer de esto una función verdaderamente genérica utilizando un TSQL dinámico construido a partir de parámetros de entrada.

/* 
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

NO puede usar SQL dinámico en una función ... –

+0

@Quandary; ¿Qué SQL dinámico? – AMissico

3

Esta solución funciona sin necesidad de implementar Visual Studio o el archivo dll en el servidor.

Copiar y pegar y funciona!

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) 
+0

Esto todavía requiere habilitar CLR en SQL Server y desplegar una DLL. – thomas

Cuestiones relacionadas