use*_*912 8 sql-server sql-server-2012
我正在尝试创建一个用户定义的函数来调用其中的系统RAND()函数,当我尝试使用以下消息创建它错误的函数时:
消息443,级别16,状态1,过程getNumber,行10
在函数内无效使用副作用运算符'rand'.
我的功能代码:
CREATE FUNCTION getNumber(@_id int)
RETURNS DECIMAL(18,4)
AS
BEGIN
DECLARE @RtnValue DECIMAL(18,4);
SELECT TOP 1 @RtnValue = EmployeeID
FROM dbo.Employees
ORDER BY EmployeeID DESC
SET @RtnValue = RAND() * @RtnValue * (1/100)
RETURN @RtnValue;
END
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个问题?
M.A*_*Ali 13
问题是您无法从用户定义的函数内部调用非确定性函数.
我通过创建一个视图来解决这个限制,在视图中调用该函数并在函数中使用该视图,类似这样......
CREATE VIEW vw_getRANDValue
AS
SELECT RAND() AS Value
Run Code Online (Sandbox Code Playgroud)
ALTER FUNCTION getNumber(@_id int )
RETURNS DECIMAL(18,4)
AS
BEGIN
DECLARE @RtnValue DECIMAL(18,4);
SELECT TOP 1 @RtnValue = EmployeeID
FROM dbo.Employees
ORDER BY EmployeeID DESC
SET @RtnValue = (SELECT Value FROM vw_getRANDValue) * @RtnValue * (1.0000/100.0000) --<-- to make sure its not converted to int
RETURN @RtnValue;
END
Run Code Online (Sandbox Code Playgroud)
只需从外部将 RAND() 值作为参数传递:
CREATE FUNCTION getNumber(@_id int, @RAND FLOAT)
RETURNS DECIMAL(18,4)
AS
BEGIN
DECLARE @RtnValue DECIMAL(18,4);
SELECT TOP 1 @RtnValue = EmployeeID
FROM dbo.Employees
ORDER BY EmployeeID DESC
SET @RtnValue = @RAND * @RtnValue * (1/100)
RETURN @RtnValue;
END
Run Code Online (Sandbox Code Playgroud)
并称为 getNumber(10, RAND())
没有任何副作用。
小心兰德!
如果你检查这个,你会看到,对这个VIEW的多次调用都返回了相同的值.这与NEWID()不同.因此,如果你真的想要随机数字,最好采取NEWID()并做一些"技巧"来获取一个数字 - 让我们说 - 第一个字节......
CREATE VIEW vw_getRANDValue
AS
SELECT RAND() AS Value
GO
CREATE VIEW vw_getNEWID
AS
SELECT NEWID() AS Value
GO
CREATE FUNCTION dbo.Test()
RETURNS TABLE AS
RETURN
WITH Numbers AS
(SELECT 1 AS x UNION SELECT 2 UNION SELECT 3)
SELECT *
,(SELECT Value FROM vw_getRANDValue) AS myRandom
,(SELECT Value FROM vw_getNEWID) AS myNewid
FROM Numbers
GO
SELECT * FROM dbo.Test();
GO
DROP FUNCTION dbo.Test;
GO
DROP VIEW vw_getRANDValue;
GO
DROP VIEW vw_getNEWID;
GO
Run Code Online (Sandbox Code Playgroud)
这是一个结果:
随机BIGINT的视图可能如下所示:
CREATE VIEW vw_getRandomBigInt
AS
SELECT CONVERT(BIGINT,CONVERT(VARBINARY(16),NEWID(),1)) * (-1) AS Value
GO
Run Code Online (Sandbox Code Playgroud)
提示:我用很多行检查过它,看起来(只是通过视觉),这种方法并不是真正随机的(所有BIGINT都有相同的宽度......).这似乎工作正常:
CREATE VIEW vw_getRandomInt
AS
SELECT sys.fn_replvarbintoint(sys.fn_cdc_hexstrtobin(LEFT(REPLACE(CONVERT(VARCHAR(100),NEWID()),'-',''),4))) AS Value
GO
Run Code Online (Sandbox Code Playgroud)