T-SQL是否有一个用于连接字符串的聚合函数?

11 浏览
0 Comments

T-SQL是否有一个用于连接字符串的聚合函数?

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

建筑名称 投票编号

------------ ----------

Foo Centre 12

Foo Centre 13

Foo Centre 14

Bar Hall 15

Bar Hall 16

Baz School 17

我需要编写一个查询,将建筑名称分组并显示一个类似于以下的投票编号列表:

建筑名称 投票编号

------------ -----------

Foo Centre 12, 13, 14

Bar Hall 15, 16

Baz School 17

如何在T-SQL中实现这个?我不想写一个存储过程,因为它似乎有点过度,但我不是一个数据库专家。似乎像SUM()或AVG()这样的聚合函数是我需要的,但我不知道T-SQL是否有这样的函数。我使用的是SQL Server 2005。

0
0 Comments

T-SQL中没有内置的函数来连接字符串,但是可以通过编写用户定义的聚合函数来实现。下面的文章提到了SQL Server示例中的这种函数:

using System;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
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](https://gist.github.com/FilipDeVos/5b7b4addea1812067b09)

这是比选择的答案更有帮助的方法。这正是我所需要的。

这个答案可以通过使用链接中给出的示例来改进,展示不需要任何奇怪依赖的函数的确切SQL。

这种方法是否需要[SqlFacet(MaxSize = -1)],如此处描述的:[http://stackoverflow.com/a/23031812/85196](http://stackoverflow.com/a/23031812/85196)?

使用这种方法,如何控制聚合的顺序?似乎顺序将由聚集索引决定。

不幸的是,用户定义的聚合不支持窗口函数中的order by,所以无法控制顺序。可以通过使用索引提示WITH (INDEX(pk_test))来强制指定顺序。

SQLCLR方法实际上不需要SqlFacet属性,因为这些属性只用于从Visual Studio中的"部署"。它们帮助Visual Studio创建"create aggregate"语句。

这是在产品中增加可维护性屏障的好方法。

为什么?你不应该直接在服务器上编辑任何内容,构建/安装程序无论是安装SQL脚本还是二进制文件都不会关心。

在我的环境中(SQL 2008R2,.NET 2.0),连接结果丢失了最后一个字符。我添加了一个额外的空格作为解决方法:writer.Write(_intermediateResult.ToString() + " ")。有人知道这个问题的原因以及如何以更清洁的方式解决吗?

我也遇到了同样的问题。它给了一个相当好的特性,如果你执行select dbo.Concatenate(field + '|'),你会丢失尾随的'|'。或者在答案中的示例中使用"+ ' '"。

例如,SQL Azure不支持SQLCLR,所以就是这样了。

应该将SQL Azure视为关系云存储,而不是完整的SQL Server实例的替代品。你应该在应用程序的业务层中进行字符串连接。这里的示例是针对那些可怕的"遗留"SQL Server部署和需要字符串连接的"糟糕"模式。

0
0 Comments

T-SQL是一种用于SQL Server数据库的查询语言,它提供了各种功能来处理和操纵数据。在T-SQL中,如果需要连接字符串,可以使用一些聚合函数来实现。然而,有些版本的SQL Server不支持特定的聚合函数来连接字符串,这就导致了问题的出现。

问题的具体内容是:T-SQL是否有一个聚合函数可以连接字符串?

问题的解决方法是根据SQL Server的版本不同而不同。对于SQL Server 2017及更高版本,可以使用STRING_AGG()函数来连接字符串。下面是一个示例代码:

SELECT
    HeaderValue, STRING_AGG(ChildValue,', ')
    FROM 
    GROUP BY HeaderValue

对于SQL Server 2005到2016的版本,需要使用FOR XML PATH子句来连接字符串。下面是一个示例代码:

SELECT
    t1.HeaderValue
        ,STUFF(
                   (SELECT
                        ', ' + t2.ChildValue
                        FROM  t2
                        WHERE t1.HeaderValue=t2.HeaderValue
                        ORDER BY t2.ChildValue
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM  t1
    GROUP BY t1.HeaderValue

需要注意的是,不是所有的FOR XML PATH连接都能正确处理XML特殊字符。在上面的示例中,varchar(或nvarchar)转换几乎总是在这个“技巧”的示例中被忽视。

此外,还提到了在使用FOR XML PATH时,可以使用('.text())[1]','varchar(max)')来代替('.', 'varchar(max)'),但具体的区别没有进一步解释。

总之,T-SQL提供了多种方法来连接字符串,但具体的解决方法取决于SQL Server的版本。对于SQL Server 2017及更高版本,可以使用STRING_AGG()函数,而对于较早的版本,则需要使用FOR XML PATH子句。

0