I am looking for generating a random number which the generated number is not there on another table.
For Example: If a table called randomNums
having the values 10,20,30,40,50
.
I like to generate a number apart from the above values.
I tried the following query.
Query
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
But sometimes this query returns nothing.
Because that time it generates the number which is there in the table randomNums
.
How to solve this issue?
Yet another option, I've always liked NEWID()
for random ordering, and cross joins create many rows very efficiently:
;with cte AS (SELECT 1 n UNION ALL SELECT 1)
,cte2 AS (SELECT TOP 100 ROW_NUMBER() OVER(ORDER BY a.n) n
FROM cte a,cte b,cte c,cte d, cte e, cte f, cte g)
SELECT TOP 1 n
FROM cte2 a
WHERE NOT EXISTS (SELECT 1
FROM randomNums b
WHERE a.n = b.num)
ORDER BY NEWID()
Demo: SQL Fiddle
If you don't want to use a WHILE
loop then you might look into this solution which employs a recursive CTE
:
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
UNION ALL
SELECT s.rn
FROM (
SELECT rn
FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums)
) t
CROSS JOIN (SELECT FLOOR(RAND()*100) AS rn) AS s
WHERE t.rn IS NULL
)
SELECT rn
FROM CTE
EDIT:
As stated in comments below the above does not work: If the first generated number (from the CTE
anchor member) is a number already present in randomNums
, then the CROSS JOIN
of the recursive member will return NULL
, hence the number from the anchor member will be returned.
Here is a different version, based on the same idea of using a recursive CTE
, that works:
DECLARE @maxAttempts INT = 100
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn,
1 AS i
UNION ALL
SELECT FLOOR(RAND(CHECKSUM(NEWID()))*100) AS rn, i = i + 1
FROM CTE AS c
INNER JOIN randomNums AS r ON c.rn = r.num
WHERE (i = i) AND (i < @maxAttempts)
)
SELECT TOP 1 rn
FROM CTE
ORDER BY i DESC
Here, the anchor member of the CTE
firstly generates a random number. If this number is already present in randomNums
the INNER JOIN
of the recursive member will succeed, hence yet another random number will be generated. Otherwise, the INNER JOIN
will fail and the recursion will terminate.
A couple of things more to note:
i
variable is used to record the number of attempts made to generate a 'unique' random number. i
is used in the INNER JOIN
operation of the recursive member so as to join with the random value of the immediately preceding recursion only . RAND()
with the same seed value return the same results, we have to use CHECKSUM(NEWID())
as the seed of RAND()
. @maxAttempts
can optionally be used to specify the maximum number of attempts made in order to generate a 'unique' random number. Query
declare @RandomNums table (Num int);
insert into @RandomNums values (10),(20),(30),(40),(50),(60),(70),(80),(90);
-- Make a table of AvailableNumbers
with N as
(
select n from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
),
AvailableNumbers as
(
select -- top 97 -- limit as you need
row_number() over(order by (select 1)) as Number
from
N n1, N n2 --, N n3, N n4, N n5, N n6 -- multiply as you need
),
-- Find which of AvailableNumbers is Vacant
VacantNumbers as
(
select
OrdinalNumber = row_number() over(order by an.Number) ,
an.Number
from
AvailableNumbers an
left join @RandomNums rn on rn.Num = an.number
where
rn.Num is null
)
-- select rundom VacantNumber by its OrdinalNumber in VacantNumbers
select
Number
from
VacantNumbers
where
OrdinalNumber = floor(rand()*(select count(*) from VacantNumbers) + 1);
Another option could be to create an unique index on num value for table randomNums. Then in your code catch the possible error if duplicated key is generated, and in that case choose another number and re-try.
Try
declare @n as int
while @n is null and (select COUNT(*) from randomNums) < 100
Begin
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT @n = rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
End
select @n
It would only be advisable to use this approach, if the number of exclusions is relatively small.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.