T-SQL是否具有连接字符串的聚合函数?

Bra*_*bby 76 t-sql sql-server group-by sql-server-2005 aggregate-functions

可能重复:
SQL Server 2000中的内嵌类型函数?
连接行值T-SQL

我有一个我正在查询的视图,看起来像这样:

BuildingName    PollNumber
------------    ----------
Foo Centre      12        
Foo Centre      13
Foo Centre      14
Bar Hall        15
Bar Hall        16
Baz School      17
Run Code Online (Sandbox Code Playgroud)

我需要编写一个将BuildingNames组合在一起的查询,并显示一个PollNumbers列表,如下所示:

BuildingName    PollNumbers
------------    -----------
Foo Centre      12, 13, 14
Bar Hall        15, 16
Baz School      17
Run Code Online (Sandbox Code Playgroud)

我怎么能在T-SQL中做到这一点?我宁愿不为此写一个存储过程,因为它看起来有点矫枉过正,但我​​不是一个数据库人.看起来像SUM()或AVG()这样的聚合函数是我需要的,但我不知道T-SQL是否有一个.我正在使用SQL Server 2005.

KM.*_*KM. 114

对于SQL Server 2017及以上使用:

STRING_AGG()

set nocount on;
declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))
insert into @YourTable VALUES (1,1,'CCC')
insert into @YourTable VALUES (2,2,'B<&>B')
insert into @YourTable VALUES (3,2,'AAA')
insert into @YourTable VALUES (4,3,'<br>')
insert into @YourTable VALUES (5,3,'A & Z')
set nocount off
SELECT
    t1.HeaderValue
        ,STUFF(
                   (SELECT
                        ', ' + t2.ChildValue
                        FROM @YourTable t2
                        WHERE t1.HeaderValue=t2.HeaderValue
                        ORDER BY t2.ChildValue
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM @YourTable t1
    GROUP BY t1.HeaderValue

SELECT
    HeaderValue, STRING_AGG(ChildValue,', ')
    FROM @YourTable
    GROUP BY HeaderValue
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

HeaderValue 
----------- -------------
1           CCC
2           B<&>B, AAA
3           <br>, A & Z

(3 rows affected)
Run Code Online (Sandbox Code Playgroud)

对于SQL Server 2005和2016年,您需要执行以下操作:

--Concatenation with FOR XML and eleminating control/encoded character expansion "& < >"
set nocount on;
declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))
insert into @YourTable VALUES (1,1,'CCC')
insert into @YourTable VALUES (2,2,'B<&>B')
insert into @YourTable VALUES (3,2,'AAA')
insert into @YourTable VALUES (4,3,'<br>')
insert into @YourTable VALUES (5,3,'A & Z')
set nocount off
SELECT
    t1.HeaderValue
        ,STUFF(
                   (SELECT
                        ', ' + t2.ChildValue
                        FROM @YourTable t2
                        WHERE t1.HeaderValue=t2.HeaderValue
                        ORDER BY t2.ChildValue
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM @YourTable t1
    GROUP BY t1.HeaderValue
Run Code Online (Sandbox Code Playgroud)

OUTPUT:

HeaderValue ChildValues
----------- -------------------
1           CCC
2           AAA, B<&>B
3           <br>, A & Z

(3 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

另外,请注意,并非所有FOR XML PATH连接都能正确处理上面示例中的XML特殊字符.

  • `.value('(./ text())[1]','nvarchar(max)')`的表现明显优于`.value('.','nvarchar(max)')`,在我的测试中13.2倍. (8认同)

Fil*_*Vos 36

Sql Server中没有内置函数,但可以通过编写用户定义的聚合来实现.本文提到了这样一个函数作为SQL Server示例的一部分:http://msdn.microsoft.com/en-us/library/ms182741.aspx

作为示例,我包含了Concatenate聚合的代码.要使用它,请在Visual Studio中创建一个数据库项目,添加新的SqlAggregate并将代码替换为下面的示例.部署后,您应该在数据库中找到一个新程序集和一个聚合函数Concatenate

using System;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Server;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000, Name = "Concatenate")]
public class Concatenate : IBinarySerialize
{
    private StringBuilder _intermediateResult;

    internal string IntermediateResult {
        get
        {
            return _intermediateResult.ToString();
        } 
    }

    public void Init()
    {
        _intermediateResult = new StringBuilder();
    }

    public void Accumulate(SqlString value)
    {
        if (value.IsNull) return;
        _intermediateResult.Append(value.Value);
    }

    public void Merge(Concatenate other)
    {
        if (null == other)
            return;

        _intermediateResult.Append(other._intermediateResult);
    }

    public SqlString Terminate()
    {
        var output = string.Empty;

        if (_intermediateResult != null && _intermediateResult.Length > 0)
            output = _intermediateResult.ToString(0, _intermediateResult.Length - 1);

        return new SqlString(output);
    }

    public void Read(BinaryReader reader)
    {
        if (reader == null) 
            throw new ArgumentNullException("reader");

        _intermediateResult = new StringBuilder(reader.ReadString());
    }

    public void Write(BinaryWriter writer)
    {
        if (writer == null) 
            throw new ArgumentNullException("writer");

        writer.Write(_intermediateResult.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您只需编写聚合查询:

create table test(
  id int identity(1,1) not null
    primary key
, class tinyint not null
, name nvarchar(120) not null )

insert into test values 
(1, N'This'),
(1, N'is'),
(1, N'just'),
(1, N'a'),
(1, N'test'),
(2, N','),
(3, N'do'),
(3, N'not'),
(3, N'be'),
(3, N'alarmed'),
(3, N','),
(3, N'this'),
(3, N'is'),
(3, N'just'),
(3, N'a'),
(3, N'test')


select dbo.Concatenate(name + ' ')
from test
group by class

drop table test
Run Code Online (Sandbox Code Playgroud)

查询的输出是:

-- Output
-- ===================
-- This is just a test
-- ,
-- do not be alarmed , this is just a test
Run Code Online (Sandbox Code Playgroud)

我把这个类和聚合打包成一个脚本,你可以在这里找到:https://gist.github.com/FilipDeVos/5b7b4addea1812067b09

  • +1这对我来说比选择的答案更有帮助.这正是我需要的. (11认同)
  • 使用此方法,如何控制项目的聚合顺序?似乎订单将由聚集索引确定. (2认同)
  • 这是为产品添加可维护性障碍的好方法. (2认同)