2

我希望能够允许指定可选参数,所以我可以重载Accumulate()方法,可以做到吗?

我想重载以允许指定分隔符,我见过其他人必须强制指定分隔符,但这种行为不适合。

CREATE AGGREGATE dbo.Concatenate (@input nvarchar(max), <OPTIONAL PARAMETER HERE>)
RETURNS nvarchar(max)

作为参考,这里是包含Accumulate()我要重载的方法的聚合类代码:

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


namespace CLR.Utilities {
    /// <summary>
    /// <list type="references">
    ///     <reference>
    ///         <name>How to: Create and Run a CLR SQL Server Aggregate</name>
    ///         <link>http://msdn.microsoft.com/en-us/library/91e6taax(v=vs.90).aspx</link>
    ///     </reference>
    ///     <reference>
    ///         <name>SqlUserDefinedAggregateAttribute</name>
    ///         <link>http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqluserdefinedaggregateattribute(v=vs.90).aspx</link>
    ///     </reference>
    ///     <reference>
    ///         <name>Invoking CLR User-Defined Aggregate Functions (Provides seed code for this function)</name>
    ///         <link>http://technet.microsoft.com/en-us/library/ms131056.aspx</link>
    ///     </reference>
    /// </list>
    /// </summary>
    [Serializable]
    [SqlUserDefinedAggregate(
        Format.UserDefined,                 //use clr serialization to serialize the intermediate result
        IsInvariantToNulls = true,          //optimizer property
        IsInvariantToDuplicates = false,    //optimizer property
        IsInvariantToOrder = false,         //optimizer property
        MaxByteSize = -1)                   //no maximum size in bytes of persisted value
    ]
    public class Concatenate : IBinarySerialize {
        /// <summary>
        /// The variable that holds the intermediate result of the concatenation
        /// </summary>
        private StringBuilder intermediateResult;

        /// <summary>
        /// Initialize the internal data structures
        /// </summary>
        public void Init() {
            this.intermediateResult = new StringBuilder();
        }

        /// <summary>
        /// Accumulate the next value, not if the value is null
        /// </summary>
        /// <param name="value"></param>
        public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value) {
            if (value.IsNull) {
                return;
            }

            this.intermediateResult.Append(value.Value.Trim()).Append(',');
        }

        /// <summary>
        /// Merge the partially computed aggregate with this aggregate.
        /// </summary>
        /// <param name="other"></param>
        public void Merge(Concatenate other) {
            this.intermediateResult.Append(other.intermediateResult);
        }

        /// <summary>
        /// Called at the end of aggregation, to return the results of the aggregation.
        /// </summary>
        /// <returns></returns>
        [return: SqlFacet(MaxSize = -1)]
        public SqlString Terminate() {
            string output = string.Empty;
            //delete the trailing comma, if any
            if (this.intermediateResult != null
                && this.intermediateResult.Length > 0) {
                output = this.intermediateResult.ToString(0, this.intermediateResult.Length - 1).Trim();
            }

            return new SqlString(output);
        }

        public void Read(BinaryReader r) {
            intermediateResult = new StringBuilder(r.ReadString());
        }

        public void Write(BinaryWriter w) {
            w.Write(this.intermediateResult.ToString().Trim());
        }
    }
}

如果可以设置可选参数,这里是我想修改的部署代码:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Concatenate]') AND type = N'AF')
DROP AGGREGATE [dbo].[Concatenate]
GO

IF  EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'CLR.Utilities' and is_user_defined = 1)
DROP ASSEMBLY [CLR.Utilities]
GO

ALTER DATABASE [DatabaseName] SET TRUSTWORTHY ON
GO

CREATE ASSEMBLY [CLR.Utilities] FROM 'C:\Path\To\File\CLR.Utilities.dll' WITH PERMISSION_SET = UNSAFE
GO

CREATE AGGREGATE [dbo].[Concatenate] (@input nvarchar(max)) RETURNS nvarchar(max)
EXTERNAL NAME [CLR.Utilities].[CLR.Utilities.Concatenate]
GO

GRANT EXECUTE ON [dbo].[Concatenate] TO PUBLIC
GO
4

3 回答 3

1

但是,您可以将参数设为强制参数,但也可以选择为 null。并且,在您的 C# 代码中测试该值是否为 null 并采取相应措施。就像是:

public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value, SqlString delimiter) {
        string _delimiter;
        if (value.IsNull) {
            return;
        }

        if (delimiter.IsNull) {
            _delimiter = string.Empty;
        }
        else {
            _delimiter = Delimiter.Value;
        }

        this.intermediateResult.Append(value.Value.Trim()).Append(_delimiter);
    }
于 2015-08-10T04:44:02.283 回答
0

据我所知,没有办法使用可选参数使 clr 函数或聚合,这很可悲。

于 2013-09-04T05:57:25.127 回答
0

有办法,虽然不是很好。您只需将分隔符添加到您要连接的列的末尾,并在它们之间添加一个 char(0)。然后在 clr 中,您可以查找 char(0) 并获取要使用的分隔符。

-- concat columns delimited by a comma 
select grp,dbo.clrConcat(surname) as names
  from people

-- concat column delimited by specified character
select grp,dbo.clrConcat(surname + char(0) + '|') as names
  from people

然后在clr代码中

''' <summary>  
''' Initialize the internal data structures  
''' </summary>  
Public Sub Init()
    Me.intermediateResult = New StringBuilder()
    delimiter = ","
End Sub

''' <summary>  
''' Accumulate the next value, not if the value is null  
''' </summary>  
''' <param name="value"></param>  
Public Sub Accumulate(ByVal value As SqlString)
    Dim Str As String
    Dim Pos As Integer

    If value.IsNull Then Return

    Pos = value.Value.IndexOf(Chr(0))
    If Pos >= 0 Then
        Str = value.Value.Substring(0, Pos)
        If Pos + 1 < value.Value.Length Then
            delimiter = value.Value.Substring(Pos + 1, 1)
        End If
    Else
        Str = value.Value
    End If

    Me.intermediateResult.Append(Str).Append(delimiter)
End Sub
于 2021-09-02T09:54:32.163 回答