【问题标题】:Does T-SQL have an aggregate function to concatenate strings? [duplicate]T-SQL 是否具有连接字符串的聚合函数? [复制]
【发布时间】:2011-06-29 04:59:08
【问题描述】:

可能的重复:
Implode type function in SQL Server 2000?
Concatenate row values T-SQL

我正在查询的视图如下所示:

BuildingName    PollNumber
------------    ----------
Foo Centre      12        
Foo Centre      13
Foo Centre      14
Bar Hall        15
Bar Hall        16
Baz School      17

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

BuildingName    PollNumbers
------------    -----------
Foo Centre      12, 13, 14
Bar Hall        15, 16
Baz School      17

如何在 T-SQL 中做到这一点?我宁愿不为此编写存储过程,因为这似乎有点矫枉过正,但我​​并不完全是数据库人员。似乎我需要像 SUM() 或 AVG() 这样的聚合函数,但我不知道 T-SQL 是否有。我使用的是 SQL Server 2005。

【问题讨论】:

标签: sql-server sql-server-2005 tsql group-by aggregate-functions


【解决方案1】:

适用于 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

输出:

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

(3 rows affected)

对于 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

输出:

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

(3 row(s) affected)

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

【讨论】:

  • 感谢 - 在这个“技巧”的示例中,varchar(或 nvarchar)转换几乎总是被忽略
  • 我很喜欢使用 ('(./text())[1]','varchar(max)') 而不是 ('.','varchar(max)') 但我没有'不知道有什么意义?!你可以查看这个:stackoverflow.com/questions/273238/…
  • .value('(./text())[1]', 'nvarchar(max)') 的性能明显优于 .value('.', 'nvarchar(max)'),在我的测试中提高了 13.2 倍。
  • 2017 版本中似乎有一个名为 STRING_AGG 的函数来解决这个问题?
【解决方案2】:

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());
    }
}

要使用它,您可以简单地编写一个聚合查询:

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

查询的输出是:

-- Output
-- ===================
-- This is just a test
-- ,
-- do not be alarmed , this is just a test

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

【讨论】:

  • +1 这对我来说比选择的答案更有帮助。这正是我所需要的。
  • @SerjSagan 我添加了代码 + 一个示例并链接到安装脚本,以便阅读此答案的人更清楚。
  • 使用这种方法,我如何控制项目聚合的顺序?似乎顺序将由聚集索引确定。
  • 你不能,不幸的是用户定义的聚合在窗口函数中不支持order by。强制排序的一个选项是使用索引提示WITH (INDEX(pk_test))
  • 这是为您的产品添加可维护性障碍的好方法。
猜你喜欢
  • 2011-02-20
  • 2017-10-27
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
相关资源
最近更新 更多