Create a User defined function like SQL server 2017 STRING_AGG on earlier versions

假装没事ソ 提交于 2019-12-12 15:39:46

问题


I try to create a generic function that can be used like this example of using the new string_agg built-in function on SQL Server 2017

the inside implementation can be something like the follow

with tbl as(
   select a.Id, c.Desc
     from TableA a
     join TableB b on b.aId = a.Id
     join TableC c on c.Code = b.bCode 
)  
select distinct ID
     , STUFF(( select ', ' + Desc from tbl t where t.ID = tbl.ID 
            for xml path(''),TYPE).value('.','VARCHAR(MAX)'),1,2,'') Desc   
from tbl

But how to receives the field key, the field to be connected, the separator char, and the scoped select context? Is it related to Inline or Multi-Statement Table-Valued Functions ?


回答1:


Well, this is an ugly hack, I have to go and wash my hands now, but it works (in a way :-D)

CREATE FUNCTION dbo.MyStringAgg(@SelectForXmlAuto XML,@Delimiter NVARCHAR(10))
RETURNS NVARCHAR(MAX)
AS
BEGIN
    RETURN STUFF((
             SELECT @Delimiter + A.nd.value(N'(@*)[1]',N'nvarchar(max)')
             FROM @SelectForXmlAuto.nodes(N'/*') AS A(nd)
             FOR XML PATH(''),TYPE
           ).value(N'.',N'nvarchar(max)'),1,LEN(@Delimiter),'');
END
GO

DECLARE @tbl TABLE(GroupId INT,SomeValue NVARCHAR(10));
INSERT INTO @tbl VALUES(1,'A1'),(1,'A2'),(2,'B1'),(3,'C1'),(3,'C2'),(3,'C3');

SELECT GroupId
      ,dbo.MyStringAgg((SELECT SomeValue 
                        FROM @tbl AS t2 
                        WHERE t2.GroupId=t.GroupId 
                        FOR XML AUTO), N', ')
FROM @tbl AS t
GROUP BY GroupId;
GO

DROP FUNCTION dbo.MyStringAgg;

The result

1    A1, A2
2    B1
3    C1, C2, C3

The parameter is a FOR XML sub-select within paranthesis. This will implicitly pass the sub-selects result as an XML into the function.

To be honest: I would not use this myself...

A query like this

SELECT GroupId
      ,STUFF((SELECT N', ' + SomeValue 
              FROM @tbl AS t2 
              WHERE t2.GroupId=t.GroupId 
              FOR XML PATH,TYPE).value(N'.','nvarchar(max)'),1,2,'')
FROM @tbl AS t
GROUP BY GroupId;

produces the same result and is almost the same amount of typing - but should be faster then calling a slow UDF...




回答2:


Ok.. so with the first comment of @MichałTurczyn I run into this Microsoft article about CLR User-Defined Aggregate - Invoking Functions

Once I compile the code into SrAggFunc.dll, I was trying to register the aggregate in SQL Server as follows:

CREATE ASSEMBLY [STR_AGG] FROM 'C:\tmp\STR_AGG.dll'; 
GO

But I got the following error.

Msg 6501, Level 16, State 7, Line 1
CREATE ASSEMBLY failed because it could not open the physical file 'C:\tmp\SrAggFunc.dll': 3(The system cannot find the path specified.).

So I used this excellant part of @SanderRijken code and then change the command to

CREATE ASSEMBLY [STR_AGG] 
FROM 0x4D5A90000300000004000000FF......000; --from GetHexString function
GO

and then,

CREATE AGGREGATE [STR_AGG] (@input nvarchar(200)) RETURNS nvarchar(max) 
EXTERNAL NAME [STR_AGG].C_STRING_AGG;`

Now it's done.

You can see it under your Database -> Programmability on SSMS

and used like :

SELECT a.Id, [dbo].[STR_AGG](c.Desc) cDesc
FROM TableA a
JOIN TableB b on b.aId = a.Id
JOIN TableC c on c.Code = b.bCode 
GROUP BY a.Id

Thanks all =)



来源:https://stackoverflow.com/questions/48783205/create-a-user-defined-function-like-sql-server-2017-string-agg-on-earlier-versio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!