_simon_ (2/1/2013)
This is my scalar function, which returns numbers between @min-2 and @max-2 (both included):
create function GetRandom(@min int, @max-2 int)
returns int
as
begin
declare @diff int = @max-2 - @min-2 + 1
return @min-2 + crypt_gen_random(1) % @diff
end
This is how I test it:
-- This runs for about 50s on my computer.
declare @t table (Number int)
declare @winners table (Number int)
declare @i int = 0
declare @iCount int = 10
declare @j-2 int = 0
declare @jCount int = 100000
while (@i < @iCount)
begin
delete from @t
set @j-2 = 0
while (@j < @jCount)
begin
insert into @t (Number)
select dbo.GetRandom(1, 3)
set @j-2 += 1
end
insert into @winners (Number)
select top 1 Number
from (
select Number, count(*) C
from @t
group by Number
) x
order by C desc
set @i += 1
end
select Number, count(*)
from @winners
group by Number
order by count(*) desc
Output (number 1 wins in ~80% cases):
Number
----------- -----------
1 9
2 1
Now almost every time I run this query, the Number 1 appears slightly more often... Is there something wrong with my GetRandom function?
Which edition and version of SQL Server are you actually using. I'm using 2k8 and I get the following error when I try to create your function...
Msg 443, Level 16, State 1, Procedure GetRandom, Line 6
Invalid use of a side-effecting operator 'Crypt_Gen_Random' within a function.
You're implitly converting a VARBINARY(1) to a 4 byte int and at the machine language level, that makes a mess. If you don't think so, change the crypt_gen_random(1) to crypt_gen_random(4) and see the "impossible" happen with the output of your test.
You can fix the problem by changing the crypt_gen_random(1) to ABS(CHECKSUM(CRYPT_GEN_RANDOM(4))).
--Jeff Moden
Change is inevitable... Change for the better is not.