正在生成 SQL 之前从未使用过的随机数
Generating never used before random number in SQL
我正在使用下面的函数生成一个介于 0 和 99999999999 之间的随机数。
CREATE VIEW [dbo].[rndView]
AS
SELECT RAND() rndResult
GO
ALTER function [dbo].[RandomPass]()
RETURNS NUMERIC(18,0)
as
begin
DECLARE @RETURN NUMERIC(18,0)
DECLARE @Upper NUMERIC(18,0);
DECLARE @Lower NUMERIC(18,0);
DECLARE @Random float;
SELECT @Random = rndResult
FROM rndView
SET @Lower = 0
SET @Upper = 99999999999
set @RETURN= (ROUND(((@Upper - @Lower -1) * @Random + @Lower), 0))
return @RETURN
end;
但是,我需要确保返回的号码以前从未在同一个应用程序中使用过。在 .net 中,我会创建一个 while 循环并一直循环,直到在存储以前使用的值的 table 中找不到返回值。有没有办法直接在 SQL 中实现相同的结果,最好不使用循环?如果没有循环就无法做到这一点,我认为在 SQL 函数中执行它仍然比在 .net 中执行多个查询请求的循环更有效。
没有不替换的随机数。
您需要存储已经在 table 中使用过的数字,我将其定义为:
create table used_random_numbers (
number decimal(11, 0) primary key
);
然后,当您创建一个新号码时,将其插入 table。
在生成数字的代码部分,使用 while
循环。在 while
循环中,检查数字是否不存在。
现在,随着数字变大,您可以做一些事情来提高效率——还有一些方法不需要记住所有以前的值。
首先,也许 UUID/GUID 就足够了。这是“随机”id 的行业标准——尽管它是一个十六进制字符串,而不是大多数数据库中的数字。确切的语法取决于数据库。
另一种方法是使用 11 位数字。前 10 位或后 10 位数字可能是 Unix 纪元时间(自 1970 年 1 月 1 日以来的秒数)——可以是明确的,也可以是在某种转换下,因此该值“看起来”是随机的。附加数字将是一个随机数字。当然,您可以将其延长到几分钟或几天,这样您就有了更多随机数字。
您需要将使用过的值存储在 table 中,并通过递归查询生成下一个值。
答案取决于您使用的 RDBMS。
下面是两个示例,在 PostgreSQL 和 MS SQL Server 中,可以解决您的问题。
PostgreSQL
首先,创建一个 table 来保存您使用的 ID :
CREATE TABLE consumed_ids (
id BIGINT PRIMARY KEY NOT NULL
);
PRIMARY KEY
不是绝对必要的,但它会
- 生成索引以加快下一次查询;
- 确保永远不会生成两个相同的 ID。
然后,使用以下查询获取新的 id :
WITH RECURSIVE T AS (
SELECT 1 AS n, FLOOR(RANDOM() * 100000000000) AS v
UNION ALL
SELECT n + 1, FLOOR(RANDOM() * 100000000000)
FROM T
WHERE EXISTS(SELECT * FROM consumed_ids WHERE id = v)
)
INSERT INTO consumed_ids
SELECT v
FROM T
ORDER BY n DESC
LIMIT 1
RETURNING id;
逻辑是只要(最后)生成的id已经被消费了,我们就生成一个新的id。 CTE的n
列只是在最后检索最后生成的id,但你也可以用它来限制生成随机数的数量(例如,give up if n > 10
) .
(使用 PostgreSQL 12.4 测试)
MS SQL 服务器
首先,创建一个 table 来保存您使用的 ID :
CREATE TABLE consumed_ids (
id BIGINT PRIMARY KEY NOT NULL
);
然后,使用以下查询获取新的 id :
WITH T AS (
SELECT 1 AS n, FLOOR(RAND() * 100000000000) AS v
UNION ALL
SELECT n + 1, FLOOR(RAND() * 100000000000)
FROM T
WHERE EXISTS(SELECT * FROM consumed_ids WHERE id = v)
)
INSERT INTO consumed_ids (id)
OUTPUT Inserted.id
SELECT TOP 1 v
FROM T
ORDER BY n DESC;
(使用 MS SQL Server 2019 测试)。
但是请注意,MS SQL 默认情况下服务器将在 100 次尝试后放弃。
我正在使用下面的函数生成一个介于 0 和 99999999999 之间的随机数。
CREATE VIEW [dbo].[rndView]
AS
SELECT RAND() rndResult
GO
ALTER function [dbo].[RandomPass]()
RETURNS NUMERIC(18,0)
as
begin
DECLARE @RETURN NUMERIC(18,0)
DECLARE @Upper NUMERIC(18,0);
DECLARE @Lower NUMERIC(18,0);
DECLARE @Random float;
SELECT @Random = rndResult
FROM rndView
SET @Lower = 0
SET @Upper = 99999999999
set @RETURN= (ROUND(((@Upper - @Lower -1) * @Random + @Lower), 0))
return @RETURN
end;
但是,我需要确保返回的号码以前从未在同一个应用程序中使用过。在 .net 中,我会创建一个 while 循环并一直循环,直到在存储以前使用的值的 table 中找不到返回值。有没有办法直接在 SQL 中实现相同的结果,最好不使用循环?如果没有循环就无法做到这一点,我认为在 SQL 函数中执行它仍然比在 .net 中执行多个查询请求的循环更有效。
没有不替换的随机数。
您需要存储已经在 table 中使用过的数字,我将其定义为:
create table used_random_numbers (
number decimal(11, 0) primary key
);
然后,当您创建一个新号码时,将其插入 table。
在生成数字的代码部分,使用 while
循环。在 while
循环中,检查数字是否不存在。
现在,随着数字变大,您可以做一些事情来提高效率——还有一些方法不需要记住所有以前的值。
首先,也许 UUID/GUID 就足够了。这是“随机”id 的行业标准——尽管它是一个十六进制字符串,而不是大多数数据库中的数字。确切的语法取决于数据库。
另一种方法是使用 11 位数字。前 10 位或后 10 位数字可能是 Unix 纪元时间(自 1970 年 1 月 1 日以来的秒数)——可以是明确的,也可以是在某种转换下,因此该值“看起来”是随机的。附加数字将是一个随机数字。当然,您可以将其延长到几分钟或几天,这样您就有了更多随机数字。
您需要将使用过的值存储在 table 中,并通过递归查询生成下一个值。
答案取决于您使用的 RDBMS。
下面是两个示例,在 PostgreSQL 和 MS SQL Server 中,可以解决您的问题。
PostgreSQL
首先,创建一个 table 来保存您使用的 ID :
CREATE TABLE consumed_ids (
id BIGINT PRIMARY KEY NOT NULL
);
PRIMARY KEY
不是绝对必要的,但它会
- 生成索引以加快下一次查询;
- 确保永远不会生成两个相同的 ID。
然后,使用以下查询获取新的 id :
WITH RECURSIVE T AS (
SELECT 1 AS n, FLOOR(RANDOM() * 100000000000) AS v
UNION ALL
SELECT n + 1, FLOOR(RANDOM() * 100000000000)
FROM T
WHERE EXISTS(SELECT * FROM consumed_ids WHERE id = v)
)
INSERT INTO consumed_ids
SELECT v
FROM T
ORDER BY n DESC
LIMIT 1
RETURNING id;
逻辑是只要(最后)生成的id已经被消费了,我们就生成一个新的id。 CTE的n
列只是在最后检索最后生成的id,但你也可以用它来限制生成随机数的数量(例如,give up if n > 10
) .
(使用 PostgreSQL 12.4 测试)
MS SQL 服务器
首先,创建一个 table 来保存您使用的 ID :
CREATE TABLE consumed_ids (
id BIGINT PRIMARY KEY NOT NULL
);
然后,使用以下查询获取新的 id :
WITH T AS (
SELECT 1 AS n, FLOOR(RAND() * 100000000000) AS v
UNION ALL
SELECT n + 1, FLOOR(RAND() * 100000000000)
FROM T
WHERE EXISTS(SELECT * FROM consumed_ids WHERE id = v)
)
INSERT INTO consumed_ids (id)
OUTPUT Inserted.id
SELECT TOP 1 v
FROM T
ORDER BY n DESC;
(使用 MS SQL Server 2019 测试)。
但是请注意,MS SQL 默认情况下服务器将在 100 次尝试后放弃。