我想要一个函数来查找传入的最大的String值列表。
我想从sql server调用它作为Select great('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满足我的要求?
答案 0 :(得分: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的修改分割函数)
注意:这几乎肯定不是最快的方法。如果您需要执行数百万次,则另一种解决方案可能更合适。
答案 1 :(得分:1)
不可以,SQL Server用户定义函数中的参数(即.NET中的params
修饰符)不可能是T-SQL或SQLCLR。是的,一些内置函数确实允许这样的事情(例如CHECKSUM(*)
),但这些函数直接构建在SQL Server中,而不是诸如用户定义函数/表值函数之类的API。
为了最好地解决这个问题的目标,你在什么情况下得到这些价值?它们是表或查询的多列吗?它们是各行吗?这些值是否已作为CSV列表连接在一起? T-SQL实际上可以很好地自行排序事物列表。您可以构建查询以使用OUTER APPLY
(FROM子句的一部分),可以在各种情况下使用它来执行此操作。例如:
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个名称字段中的“最大”值。正如您所看到的,此方法足够灵活,可包含任意数量的列。
答案 2 :(得分:0)
不幸的是,没有可能在CLR中声明UDF,你想要签名(params SqlString [] p)。 UDF只能有强类型定义的params列表,以及关键字" 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需要有唯一的名称。
如果你有/想要无限数量的参数,那么在你的UDF结束时,它无法在.CLR中实现。它可以只是固定数量的参数,例如。 4(如你提到的情况)。
在这种情况下,为什么在SP上使用CLR的原因要好得多。但我也要指出,这并不意味着你可以通过.CLR获得更好的性能。在某些情况下,T-SQL / PS的性能会更好。 当然,这里的一切都取决于您最终可以在生产环境中部署.CLR的假设。如果我可以将.CLR部署到生产中并需要数学,字符串操作或类似的东西,我总是使用CLR。