SQL Server 中的自定义聚合函数 (concat)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4374709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-01 08:30:22  来源:igfitidea点击:

Custom aggregate function (concat) in SQL Server

sqlsql-server-2005sql-server-2008aggregate-functionsaggregation

提问by Stefan Steiger

Question: I want to write a custom aggregate function that concatenates string on group by.

问题:我想编写一个自定义聚合函数来连接 group by 上的字符串。

So that I can do a

这样我就可以做一个

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

All I find is SQL CRL aggregate functions, but I need SQL, without CLR.

我发现的只是 SQL CRL 聚合函数,但我需要 SQL,没有 CLR。



Edit:1
The query should look like this:



编辑:1
查询应如下所示:

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



Edit 2:
It is true that it isn't possible without CLR.
However, the subselect answer by astander can be modified so it doesn't XML-encode special characters.



编辑 2:
确实,没有 CLR 是不可能的。
但是,可以修改 astander 的 subselect 答案,因此它不会对特殊字符进行 XML 编码。

The subtle change for this is to add this after "FOR XML PATH": ,

对此的细微变化是在“FOR XML PATH”之后添加:

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

Here a few examples

这里有几个例子

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   

采纳答案by Damien_The_Unbeliever

You cannot write custom aggregates outside of the CLR.

您不能在 CLR 之外编写自定义聚合。

The only type of functions you can write in pure T-SQL are scalar and table valued functions.

您可以在纯 T-SQL 中编写的唯一类型的函数是标量和表值函数。

Compare the pages for CREATE AGGREGATE, which only lists CLR style options, with CREATE FUNCTION, which shows T-SQL and CLR options.

CREATE AGGREGATE的页面(仅列出 CLR 样式选项)与CREATE FUNCTION 的页面(显示 T-SQL 和 CLR 选项)进行比较。

回答by Adriaan Stander

Have a look at something like. This is not an aggregate function. If you wish to implement your own aggregate function, it will have to be CLR...

看看类似的东西。这不是聚合函数。如果您希望实现自己的聚合函数,则必须是 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

回答by Konstantin Salavatov

Starting from 2017 there is built-in concatenate aggregate function STRING_AGG :)

从 2017 年开始,有内置的连接聚合函数 STRING_AGG :)

https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017

https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017

回答by kevchadders

Found this linkaround concatenation which covers methods like

找到了这个关于连接的链接,它涵盖了类似的方法

Concatenating values when the number of items are not known

当项目数未知时连接值

  • Recursive CTE method
  • The blackbox XML methods
  • Using Common Language Runtime
  • Scalar UDF with recursion
  • Table valued UDF with a WHILE loop
  • Dynamic SQL
  • The Cursor approach
  • 递归 CTE 方法
  • 黑盒 XML 方法
  • 使用公共语言运行时
  • 带递归的标量 UDF
  • 带有 WHILE 循环的表值 UDF
  • 动态 SQL
  • 光标方法

Non-reliable approaches

不可靠的方法

  • Scalar UDF with t-SQL update extension
  • Scalar UDF with variable concatenation in SELECT
  • 带有 t-SQL 更新扩展的标量 UDF
  • SELECT 中带有变量连接的标量 UDF

Though it doesn't cover aggerate functions there may be some use around concatenation in there to help you with your problem.

虽然它不包括 aggerate 函数,但在那里可能会使用一些连接来帮助您解决问题。

回答by Xilmiki

This solution works with no need of deploy from Visual studio or dll file in server.

此解决方案无需从 Visual Studio 或服务器中的 dll 文件进行部署。

Copy-Paste and it Work!

复制粘贴和它的工作!

http://groupconcat.codeplex.com/

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 )

回答by Bijimon

You could do something like what I have done below to create a custom aggregate concatenation function in pure T-SQL. Obviously I have gone with a hard coded table name and group by column but it should illustrate the approach. There is probably some way to make this a truly generic function using dynamic TSQL constructed from input parameters.

您可以像我在下面所做的那样在纯 T-SQL 中创建自定义聚合连接函数。显然,我使用了硬编码的表名和按列分组,但它应该说明了该方法。可能有一些方法可以使用从输入参数构造的动态 TSQL 使其成为真正的通用函数。

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