我有一套,我要执行两个单独的随机抽奖拉。我想在没有循环声明的情况下做这个抽奖,并认为CTE工作得很好。因为他们总是为第一次随机选择工作。但是,当我尝试链接相同逻辑的第二次重复时,我开始从数据库中获取随机行为。具体来说,我的upperRange2字段有时是NULL。运行下面的代码几次,你应该看到,有时upperRange2有适当的值,有时它只是NULL。如果您只从candidateSelected CTE中选择,您将看到upperRange字段始终有效。这个问题只发生在我为两个不同的随机选择尝试遵循这个模式两次。为什么我的upperRange2列偶尔会返回NULL。我滥用CTE吗?
CREATE VIEW [dbo].[RandomNumberView]
AS
SELECT RAND() AS randomNumber
GO
CREATE FUNCTION [dbo].[GetRandoms] (
@lowerRange int,
@upperRange int,
@count int
)
RETURNS @randoms TABLE (
[randomNumber] int primary key
)
AS
BEGIN
IF (@lowerRange IS NOT NULL
AND @upperRange IS NOT NULL
AND @count IS NOT NULL
)
BEGIN
DECLARE @candidateCount int,
@random float,
@selected int
/* This is the numbers that are possible to be selected
** from the user specified range. */
SELECT @candidateCount = (@upperRange - @lowerRange) + 1
/* If the user specified a count that is greater than the
** candidate count, then return every possibility between
** the lower and upper range even though its less than
** the count requested. */
IF (@count > @candidateCount)
INSERT @randoms
SELECT i
FROM Seq(@lowerRange, @upperRange)
/* So that we don't select duplicate numbers keep grabbing
** a unique random number until the user specified count
** has been reached from the range specified. */
WHILE (@count <= @candidateCount AND (SELECT COUNT(*) FROM @randoms) < @count)
BEGIN
/* Note the use of the RandomNumberView. It is forbidden
** to use non-deterministic functions in functions, which
** is why there isn't a call to RAND() here instead. The
** RandomNumberView is just a boxing mechanism around the
** RAND() function so that it turns it into a table type
** source instead of a function and is therefore allowed. */
SELECT @random = randomNumber FROM RandomNumberView
/* To understand how the percentile random number is reduced
** to the range specified by the user consider this statement
** that produces a range of 0 to 6: ROUND(RAND() * 6, 0) */
SELECT @selected = ROUND(@random * (@candidateCount - 1), 0) + @lowerRange
IF (NOT EXISTS (SELECT * FROM @randoms WHERE randomNumber = @selected))
INSERT @randoms VALUES (@selected)
END
END
RETURN
END
GO
declare @candidates table (name varchar(10) primary key, [weight] int not null, secondaryWeight int not null);
insert @candidates values ('Carl', 2, 1);
insert @candidates values ('James', 1, 2);
insert @candidates values ('Randy', 3, 1);
insert @candidates values ('David', 2, 2);
insert @candidates values ('Michael', 1, 1);
declare @pickCount int = 2;
with
candidateRows as (
select
name,
[weight],
secondaryWeight,
row_number() over (order by [weight]) as [row]
from @candidates
),
candidateLowerRanges as (
select
name,
[weight],
secondaryWeight,
[row],
(
select sum([weight])
from candidateRows b
where b.[row] <= a.[row]
) as upperRange
from candidateRows a
),
candidateFullRanges as (
select
name,
[weight],
secondaryWeight,
[row],
upperRange,
lag(upperRange, 1, 0) over (order by upperRange) as previousUpperRange
from candidateLowerRanges
),
candidatesSelected as (
select
name,
[weight],
secondaryWeight,
[row],
upperRange,
previousUpperRange,
randomNumber
from candidateFullRanges s
inner join GetRandoms(1, (select max(upperRange) from candidateLowerRanges), @pickCount) r
on s.upperRange >= r.randomNumber
and s.previousUpperRange < r.randomNumber
),
secondRows as (
select
name,
[weight],
secondaryWeight,
[row],
upperRange,
previousUpperRange,
randomNumber,
row_number() over (order by secondaryWeight desc) as [row2]
from candidatesSelected
),
secondUpperRanges as (
select
name,
[weight],
secondaryWeight,
[row],
upperRange,
previousUpperRange,
randomNumber,
row2,
(
select sum(secondaryWeight)
from secondRows b
where b.[row] <= a.[row]
) as upperRange2
from secondRows a
)
select *
from secondUpperRanges
“禁止在函数中使用非确定性函数”?你知道为什么是这样吗? SQL Server正在这样做强制执行函数的确定性,并且**可能无法第二次用相同的参数调用用户定义的函数**,从而有效地重用'rand()'中的先前值。 –
@ShannonSeverance,我没有意识到它总是把所有的功能当作确定性的。由于我没有看到任何方式来标记它,所以我认为引擎正在分析它,以确定它是否有任何非确定性函数在其中使用。感谢您指点我可能回到真正的问题,因为我误以为它有两次按顺序使用它。 – Rob
此外,我会标记你的答案是正确的,但它的评论。我刚进去并取消了GetRandoms函数的使用,并且行为结束了。 – Rob