Sql server 创建参数数目可变的CLR UDF

Sql server 创建参数数目可变的CLR UDF,sql-server,sqlclr,user-defined-functions,Sql Server,Sqlclr,User Defined Functions,我想要一个函数来查找传入的字符串值列表中最大的一个 我想从SQLServer中调用它作为selectmagest('Abcd','Efgh','Zxy','EAD')。 它应该返回Zxy。 参数的数量是可变的。顺便说一句,它与 甲骨文最大的功能。 因此,我编写了一个非常简单的CLR函数(Vs2008),并尝试部署它。 见下文 public partial class UserDefinedFunctions { [Microsoft.SqlServer.Server.SqlFunction] p

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

我想从SQLServer中调用它作为selectmagest('Abcd','Efgh','Zxy','EAD')。 它应该返回Zxy。 参数的数量是可变的。顺便说一句,它与 甲骨文最大的功能。 因此,我编写了一个非常简单的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是否可以满足我的要求?

下面是一个使用表值函数的解决方案:

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
测试:

(从中修改了拆分函数)


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

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

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
测试:

(从中修改了拆分函数)


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

不幸的是,不可能在CLR中声明带有您想要的签名的UDF(params 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)));
    }
如果希望获得更多参数,则需要添加另一个自定义项

[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。

不幸的是,不可能在CLR中声明带有您想要的签名的UDF(params 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)));
    }
如果希望获得更多参数,则需要添加另一个自定义项

[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。

否,在SQL Server用户定义函数中不可能有数量可变的参数(即.NET中的
params
修饰符),无论它们是T-SQL还是SQLCLR。是的,某些内置函数确实允许这样做(例如,
校验和(*)
),但这些函数直接内置到SQL Server中,而不是用户定义函数/表值函数等API中

为了最好地解决这个问题的目标,您是在什么背景下获得这些价值观的?它们是表或查询的多列吗?它们是不同的排吗?这些值是否已作为CSV列表连接在一起?事实上,T-SQL本身在对事物列表进行排序方面做得相当好。您可以使用
outerapply
(子句的一部分)来构造查询,这可以在各种场景中使用。例如:

选择tab.name作为[TableName],
ind.name作为[IndexName],
列名称为[列名称],
最大值。项为[最大值()]
从sys.tables选项卡
左连接(sys.ind)
内部联接sys.index\u列indcol
在indcol上。[object\u id]=ind.[object\u id]
和indcol.index\u id=ind.index\u id
内部联接sys.col列
在列[object\u id]=indcol[object\u id]上
和col.column\u id=indcol.column\u id
)
在ind.[object\u id]=tab.[object\u id]
外部应用(选择前1个tmp.Name作为[项目]
从(
选择tab.name联合所有选择ind.name联合所有选择col.name
)tmp(名称)
按tmp订购。名称ASC
)伟大的

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

否,在SQL Server用户定义函数中不可能有数量可变的参数(即.NET中的
params
修饰符),无论它们是T-SQL还是SQLCLR。是的,某些内置函数确实允许这样做(例如,
校验和(*)
),但这些函数直接内置到SQL Server中,而不是用户定义函数/表值函数等API中

为了最好地解决这个问题的目标,您是在什么背景下获得这些价值观的?它们是表或查询的多列吗?它们是不同的排吗?这些值是否已作为CSV列表连接在一起?T-SQL实际上在对li进行排序方面做得相当好