【问题标题】:Creating a CLR UDF with variable number of parameters创建具有可变数量参数的 CLR UDF
【发布时间】:2010-05-24 05:27:07
【问题描述】:

我想要一个函数来查找传入的字符串值列表中的最大值。

我想从 sql server 调用它作为 Select best('Abcd','Efgh','Zxy','EAD')。 它应该返回 Zxy。 参数的数量是可变的。顺便说一下,它非常类似于 oracle GREATEST 函数。 于是我写了一个很简单的CLR函数(Vs2008)并尝试部署。 见下文

public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString Greatest(params SqlString[] p)
{
SqlString max=p[0];
foreach (string s in p)
max = s.CompareTo(max) > 0 ? s : max;

return max;

}
};

但是当我尝试编译或部署它时,我收到以下错误 找不到数据类型 SqlString[]。

是否可以使用 SQL CLR 满足我的要求?

【问题讨论】:

  • 为什么一定是CLR函数?

标签: sql-server sqlclr user-defined-functions


【解决方案1】:

这是使用表值函数的解决方案:

CREATE FUNCTION fn_Split
(
    @text VARCHAR(8000), 
    @delimiter VARCHAR(20) = ','
)
    RETURNS @Strings TABLE 
        (
            position INT IDENTITY PRIMARY KEY,
            value VARCHAR(8000)
        )
AS BEGIN
    DECLARE @index int
    SET @index = -1

    WHILE (LEN(@text) > 0) BEGIN
        -- Find the first delimiter
        SET @index = CHARINDEX(@delimiter , @text)

        -- No delimiter left?
        -- Insert the remaining @text and break the loop
        IF (@index = 0) AND (LEN(@text) > 0) BEGIN  
            INSERT INTO @Strings VALUES (LTRIM(RTRIM(@text)))
            BREAK 
        END 

        -- Found a delimiter
        -- Insert left of the delimiter and truncate the @text
        IF (@index > 1) BEGIN
            INSERT INTO @Strings VALUES (LTRIM(RTRIM(LEFT(@text, @index - 1))))
            SET @text = RIGHT(@text, (LEN(@text) - @index))
        END
        -- Delimiter is 1st position = no @text to insert
        ELSE SET @text = RIGHT(@text, (LEN(@text) - @index))
    END
    RETURN
END
GO

测试:

DECLARE @test varchar(120)

SET @test = 'Abcd, Efgh, Zxy, EAD'

SELECT Top(1) value FROM dbo.fn_Split(@test, ',')
ORDER BY value DESC

GO

(从here修改拆分功能)

注意:这几乎肯定不是最快的方法。如果您需要执行此操作数百万次,则另一种解决方案可能更合适。

【讨论】:

    【解决方案2】:

    不,无论是 T-SQL 还是 SQLCLR,SQL Server 用户定义函数中都不能有可变数量的参数(即 .NET 中的 params 修饰符)。是的,一些内置函数确实允许这样的事情(例如 CHECKSUM(*) ),但这些是直接内置到 SQL Server 中的,而不是像用户定义函数/表值函数这样的 API。

    为了最好地解决这个问题的目标,您在什么情况下获得这些值?它们是表或查询的多个列吗?它们是不同的行吗?这些值是否已经作为 CSV 列表连接在一起? T-SQL 实际上在对事物列表进行排序方面做得非常好。您可能能够构建查询以使用 OUTER APPLYFROM 子句的一部分),它可用于在各种情况下执行此操作。例如:

    SELECT tab.name AS [TableName],
           ind.name AS [IndexName],
           col.name AS [ColumnName],
           greatest.Item AS [GREATEST()]
    FROM   sys.tables tab
    LEFT JOIN (sys.indexes ind
        INNER JOIN sys.index_columns indcol
                ON indcol.[object_id] = ind.[object_id]
               AND indcol.index_id = ind.index_id
        INNER JOIN sys.columns col
                ON col.[object_id] = indcol.[object_id]
               AND col.column_id = indcol.column_id
              )
           ON ind.[object_id] = tab.[object_id]
    OUTER APPLY (SELECT TOP 1 tmp.Name AS [Item]
                 FROM (
                        SELECT tab.name UNION ALL SELECT ind.name UNION ALL SELECT col.name
                      ) tmp(Name)
                 ORDER BY tmp.Name ASC
                ) greatest
    

    结果是每行 3 个名称字段中的“最大”值。如您所见,此方法足够灵活,可以包含任意数量的列。

    【讨论】:

      【解决方案3】:

      不幸的是,不可能在 CLR 中声明 UDF,并带有您想要的签名(参数 SqlString[] p)。 UDF 只能有强类型定义的参数列表,目前不支持关键字“params”(我希望将来也会改变)。

      这里是 String.Format UDF 的例子。

      [SqlFunction(DataAccess = DataAccessKind.None)]
          public static SqlString clr_StringFormat2(SqlString format, object s1, object s2)
          {
              return format.IsNull ? SqlString.Null : new SqlString(string.Format(format.Value, SqlTypeToNetType(s1, s2)));
          }
      

      如果您需要更多参数,则需要添加另一个 UDF。

      [SqlFunction(DataAccess = DataAccessKind.None)]
          public static SqlString clr_StringFormat3(SqlString format, object s1, object s2, object s3)
          {
              return format.IsNull ? SqlString.Null : new SqlString(string.Format(format.Value, SqlTypeToNetType(s1, s2, s3)));
          }
      

      .CLR 中要记住的另一件事是没有方法重载,因此您的 UDF 需要具有唯一名称。

      最后,如果您拥有/想要无限数量的参数,则无法在 .CLR 中实现 UDF。它只能是固定数量的参数,例如。 4(如您提到的情况)。

      在这种情况下使用 CLR 而不是 SP 的原因是性能要好得多。但我还要指出,这并不意味着使用 .CLR 可以在所有可能的情况下获得更好的性能。在某些情况下,T-SQL/PS 会执行得更好。 当然,这里的一切都取决于您最终可以在生产环境中部署 .CLR 的假设。如果我可以将 .CLR 部署到生产环境并需要数学、字符串操作或类似的东西,我总是使用 CLR。​​

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2012-09-18
        • 2013-08-26
        • 1970-01-01
        • 1970-01-01
        • 2018-07-19
        相关资源
        最近更新 更多