我试图定义此功能:
CREATE FUNCTION getRandomName ()
RETURNS VARCHAR(48)
AS BEGIN
-- concatenate two random strings from two columns in a table and return as a new string
DECLARE @finalStr VARCHAR(48);
SET @finalStr = (SELECT TOP 1 st1 FROM randomStrings ORDER BY RAND()) +
' ' +
(SELECT TOP 1 st2 FROM randomStrings ORDER BY RAND());
RETURN @finalStr;
END
我不能这样做是因为:
Msg 443, Level 16, State 1, Procedure getRandomName, Line 6
Invalid use of a side-effecting operator 'rand' within a function.
我在网上发现的与此问题相关的帖子建议在调用函数时传递随机值作为参数,或者使用视图并在函数中查询该视图以将单个随机数转换为变量。我无法使用这些方法,因为我试图在ORDER BY子句中使用随机化。
有没有办法实现这个目标?
(SQL Server 2014)
编辑:
所以你可以使用视图来获得如下所述的结果,但现在我发现自己需要将一个参数传递给函数:
CREATE FUNCTION getRandomName (
@maxPieceSize int
)
RETURNS VARCHAR(48)
AS BEGIN
-- concatenate two random strings from two columns in a table and return as a new string
DECLARE @finalStr VARCHAR(48);
SET @finalStr = (SELECT TOP 1 st1 FROM randomStrings WHERE LEN(st1) <= @maxPieceSize ORDER BY RAND()) +
' ' +
(SELECT TOP 1 st2 FROM randomStrings WHERE LEN(st1) <= @maxPieceSize ORDER BY RAND());
RETURN @finalStr;
END
因此,我无法为此方案创建视图,因为您无法将参数传递给视图。
所以这就是我的困境:
EXECUTE getRandomName(6)
或SELECT getRandomName(6)
之类的操作我是不是坚持使用程序并且这样做了#34;艰难的方式&#34; (使用输出变量,每次我想使用该方法时都必须声明该变量)?
再次编辑:
我尝试将实际方法编写为存储过程,然后从声明变量的函数调用该存储过程,分配它然后返回它。这很有道理。除了....
Msg 557, Level 16, State 2, Line 1
Only functions and some extended stored procedures can be executed from within a function.
我猜测SQL Server 确实并不希望我拥有一个可以返回随机值的函数。 (有趣,因为它本身并不是RAND()
一个功能吗?)
答案 0 :(得分:2)
为什么你坚持使用功能?将视图用作函数:
CREATE view getRandomName
AS
SELECT (SELECT TOP 1 st1 FROM randomStrings ORDER BY Newid()) +
' ' +
(SELECT TOP 1 st1 FROM randomStrings ORDER BY Newid())
as RandomName
GO
SELECT (SELECT RandomName FROM getRandomName) + ' - This is random name'
GO
在存储过程中还有一种古老而疯狂的方法来获取随机行:
CREATE PROCEDURE usp_Random_Message
@i INT
AS
SELECT TOP 1 * FROM (
SELECT TOP (@i) * FROM sys.Messages
ORDER BY message_id
) AS a ORDER BY message_id DESC
GO
DECLARE @i INT = CAST(RAND() * 100 as INT);
EXEC usp_Random_Message @i;
答案 1 :(得分:0)
首先,
SELECT TOP 1 st1 FROM randomStrings ORDER BY RAND()
不会返回您期望的内容,因为RAND
是运行时常量。这意味着服务器生成一次随机数,并在查询期间使用它。
您希望以随机顺序排列所有行,然后选择顶行。以下查询将执行此操作:
SELECT TOP 1 st1 FROM randomStrings ORDER BY NEWID()
或
SELECT TOP 1 st1 FROM randomStrings ORDER BY CRYPT_GEN_RANDOM(4)
如果您查看执行计划,您会看到randomStrings
表已完整扫描,然后排序并选择一个顶行。
我猜你想要使用你的功能:
SELECT
SomeTable.SomeColumn
,dbo.GetRandomName() AS RandomName
FROM SomeTable
对于SomeTable
中的每一行,您希望获得一些随机字符串。
即使您通过一些技巧使您的原始方法工作,您也可以完整地扫描randomStrings
表并对SomeTable
的每一行进行排序(两次)。这可能效率不高。
提高效率并避免欺骗的一种方法是确保randomStrings
表的int
列ID
的值从1到此表中的最大行数。也把它作为主键。
然后你的函数将接受两个参数 - 在1..N范围内的两个随机数,该函数将使用给定的ID构建随机字符串。
该功能可能如下所示:
CREATE FUNCTION dbo.GetRandomName
(
@ParamID1 int
,@ParamID2 int
)
RETURNS VARCHAR(48)
AS
BEGIN
DECLARE @FinalStr VARCHAR(48);
SET @FinalStr =
(SELECT st1 FROM randomStrings WHERE ID = @ParamID1)
+ ' ' +
(SELECT st1 FROM randomStrings WHERE ID = @ParamID2)
;
RETURN @FinalStr;
END
如果randomStrings
表有100行,ID为1到100,那么此函数的用法可能如下所示:
SELECT
SomeTable.SomeColumn
,dbo.GetRandomName(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 100 + 1
,(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 100 + 1
) AS RandomName
FROM SomeTable
CRYPT_GEN_RANDOM(4)
生成4个随机字节,它们被强制转换为int
并转换为0到1之间的浮点数,乘以randomStrings
表中的行数( 100)。它只是生成1 ... N
CRYPT_GEN_RANDOM
每次调用时都会生成一个不同的随机数,并且每行调用两次,因此您应该得到预期的结果。