正在生成 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 不是绝对必要的,但它会

  1. 生成索引以加快下一次查询;
  2. 确保永远不会生成两个相同的 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 次尝试后放弃。