使用MySQL生成随机且唯一的8个字符串

fun*_*ein 95 mysql sql

我正在开发一款涉及车辆的游戏.我有一个名为"车辆"的MySQL表,其中包含有关车辆的数据,包括存储车辆牌照的"板"栏.

现在这里出现了我遇到问题的部分.我需要在创建新车辆之前找到未使用的车牌 - 它应该是一个字母数字8字符随机字符串.我是如何实现这一点的是在Lua中使用while循环,这是我正在编程的语言,用于生成字符串并查询DB以查看它是否被使用.然而,随着车辆数量的增加,我预计现在这种情况会变得更加低效.因此,我决定尝试使用MySQL查询解决此问题.

我需要的查询应该只生成一个8字符的字母数字字符串,该字符串不在表中.我再次想到了生成和检查循环方法,但我并没有将这个问题局限于那个问题,以防万一更有效.我已经能够通过定义一个包含所有允许的字符的字符串并随机地对它进行子字符串来生成字符串,仅此而已.

任何帮助表示赞赏.

Ran*_*eed 92

正如我在评论中所说,我不会为碰撞的可能性而烦恼.只需生成一个随机字符串并检查它是否存在.如果是这样,请再试一次,除非已经分配了大量的印版,否则你不应该多做几次.

另一种在pure(My)SQL中生成8个字符长的伪随机字符串的解决方案:

SELECT LEFT(UUID(), 8);
Run Code Online (Sandbox Code Playgroud)

您可以尝试以下(伪代码):

DO 
    SELECT LEFT(UUID(), 8) INTO @plate;
    INSERT INTO plates (@plate);
WHILE there_is_a_unique_constraint_violation
-- @plate is your newly assigned plate number
Run Code Online (Sandbox Code Playgroud)

  • 请注意,UUID的前8个字符不是随机的,而是顺序的,因为它基于时间戳. (20认同)
  • @MartinAJ,因为该字符串是[uuid](https://en.wikipedia.org/wiki/Universally_unique_identifier#Format)。您可以轻松地替换连字符,例如`SELECT LEFT(REPLACE(UUID(),'-',''),16);` (3认同)

Eug*_*eck 79

这个问题包含两个非常不同的子问题:

  • 字符串必须看似随意
  • 字符串必须是唯一的

虽然很容易实现随机性,但没有重试循环的唯一性则不然.这使我们首先专注于独特性.非随机的唯一性可以通过以下方式实现AUTO_INCREMENT.所以使用保留唯一性,伪随机变换就可以了:

  • @paul建议使用Hash
  • AES加密也适合
  • 但是有一个很好的一个:RAND(N)本身!

保证由同一种子创建的一系列随机数

  • 重复性
  • 前8次迭代不同
  • 如果种子是INT32

所以我们使用@ AndreyVolk或@ GordonLinoff的方法,但是使用种子 INT32:

例如,Assumin RAND是一个AUTO_INCREMENT col:

INSERT INTO vehicles VALUES (blah); -- leaving out the number plate
SELECT @lid:=LAST_INSERT_ID();
UPDATE vehicles SET numberplate=concat(
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@lid)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
  substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand(@seed)*36+1, 1)
)
WHERE id=@lid;
Run Code Online (Sandbox Code Playgroud)

  • 我只是想知道.为什么使用这些数字..4294967296))*36 + 1? (7认同)
  • 这有点旧,但我想注意我必须在第二个子串参数周围添加`FLOOR()`:````substring('ABC ... 789',floor(rand(@seed:= ...)*36 + 1),1),````在某些情况下,子串试图选择字符36.9,当向上舍入到37时,将导致没有选择字符. (6认同)
  • 如果字符串可重现,则不能随机调用字符串.重复也是可能的,因为你正在使用`floor()`.这个[sqlfiddle](http://sqlfiddle.com/#!9/78abee/1)显示,为三个字符长的字符串创建了重复项. (3认同)
  • @EugenRieck我不明白你是如何得到你的数字的("前2 ^ 32次迭代").但我只需要一个重复的例子来反驳这个概念.对于ID"193844"和"775771",您的算法将生成相同的字符串"T82X711"([demo](http://sqlfiddle.com/#!9/9eecb7d/78363)). (3认同)

pau*_*aul 52

如何计算连续整数的MD5(或其他)哈希值,然后取前8个字符.

MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e
Run Code Online (Sandbox Code Playgroud)

等等

警告:我不知道你可以在碰撞之前分配多少(但它将是一个已知的恒定值).

编辑:现在这是一个古老的答案,但我再次看到它的时间在我的手上,所以,从观察......

所有数字的机会= 2.35%

所有字母的机会= 0.05%

MD5(82945)="7b763dcb ..."时的第一次碰撞(与MD5(25302)相同的结果)

  • 它根本不是随机的. (6认同)
  • 好主意,可以在主键上使用它.对于未来的项目,请记住这一点,谢谢! (2认同)
  • 只有数字才有可能产生这种情况 (2认同)
  • 这当然只是玩具随机性. (2认同)

Pad*_*ann 28

创建一个随机字符串

这是一个用于创建给定长度的随机字符串的MySQL函数.

DELIMITER $$

CREATE DEFINER=`root`@`%` FUNCTION `RandString`(length SMALLINT(3)) RETURNS varchar(100) CHARSET utf8
begin
    SET @returnStr = '';
    SET @allowedChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    SET @i = 0;

    WHILE (@i < length) DO
        SET @returnStr = CONCAT(@returnStr, substring(@allowedChars, FLOOR(RAND() * LENGTH(@allowedChars) + 1), 1));
        SET @i = @i + 1;
    END WHILE;

    RETURN @returnStr;
END
Run Code Online (Sandbox Code Playgroud)

用法SELECT RANDSTRING(8)返回一个8字符串.

你可以自定义@allowedChars.

不保证唯一性 - 正如您在其他解决方案的评论中看到的那样,这是不可能的.相反,你需要生成一个字符串,检查它是否已经被使用,如果是,则再试一次.


检查随机字符串是否已被使用

如果我们想要将碰撞检查代码保留在应用程序之外,我们可以创建一个触发器:

DELIMITER $$

CREATE TRIGGER Vehicle_beforeInsert
  BEFORE INSERT ON `Vehicle`
  FOR EACH ROW
  BEGIN
    SET @vehicleId = 1;
    WHILE (@vehicleId IS NOT NULL) DO 
      SET NEW.plate = RANDSTRING(8);
      SET @vehicleId = (SELECT id FROM `Vehicle` WHERE `plate` = NEW.plate);
    END WHILE;
  END;$$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案,明确而且恰到好处,对我来说工作得很好,谢谢@ paddy-mann (4认同)

Gor*_*off 23

这是一种方法,使用alpha数字作为有效字符:

select concat(substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1),
              substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', rand()*36+1, 1)
             ) as LicensePlaceNumber;
Run Code Online (Sandbox Code Playgroud)

请注意,不保证唯一性.你必须单独检查.

  • 使用floor(rand()*36 + 1)代替.否则一些结果将是"短暂的". (6认同)
  • 应该有35 + 1而不是36 + 1!否则,您将获得一些包含8个字符的字符串,其中一些包含7个字符 (2认同)

bei*_*lex 22

这是生成随机字符串的另一种方法:

SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 8) AS myrandomstring


And*_*olk 14

您可以使用MySQL的rand()char()函数:

select concat( 
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97),
    char(round(rand()*25)+97)
) as name;
Run Code Online (Sandbox Code Playgroud)


Pau*_*gel 13

您可以使用以下命令生成随机字母数字字符串:

lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0);
Run Code Online (Sandbox Code Playgroud)

您可以在BEFORE INSERT触发器中使用它并在while循环中检查重复:

CREATE TABLE `vehicles` (
    `plate` CHAR(8) NULL DEFAULT NULL,
    `data` VARCHAR(50) NOT NULL,
    UNIQUE INDEX `plate` (`plate`)
);

DELIMITER //
CREATE TRIGGER `vehicles_before_insert` BEFORE INSERT ON `vehicles`
FOR EACH ROW BEGIN

    declare str_len int default 8;
    declare ready int default 0;
    declare rnd_str text;
    while not ready do
        set rnd_str := lpad(conv(floor(rand()*pow(36,str_len)), 10, 36), str_len, 0);
        if not exists (select * from vehicles where plate = rnd_str) then
            set new.plate = rnd_str;
            set ready := 1;
        end if;
    end while;

END//
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

现在只需插入您的数据即可

insert into vehicles(col1, col2) values ('value1', 'value2');
Run Code Online (Sandbox Code Playgroud)

触发器将为plate列生成一个值.

(sqlfiddle演示)

如果列允许NULL,则以这种方式工作.如果您希望它为NOT NULL,则需要定义默认值

`plate` CHAR(8) NOT NULL DEFAULT 'default',
Run Code Online (Sandbox Code Playgroud)

如果大写字母数字不是你想要的,你也可以在触发器中使用任何其他随机字符串生成算法.但触发器将照顾到唯一性.


小智 8

对于由 8 个随机数和大小写字母组成的字符串,这是我的解决方案:

LPAD(LEFT(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(RAND()))), "/", ""), "+", ""), "=", ""), 8), 8, 0)
Run Code Online (Sandbox Code Playgroud)

从里到外解释:

  1. RAND 生成 0 到 1 之间的随机数
  2. MD5 计算 (1) 的 MD5 总和,32 个字符来自 af 和 0-9
  3. UNHEX 将 (2) 转换为 16 个字节,值从 00 到 FF
  4. TO_BASE64 将 (3) 编码为 base64,来自 az 和 AZ 以及 0-9 的 22 个字符加上“/”和“+”,后跟两个“=”
  5. 这三个REPLACEs 删除了 (4) 中的“/”、“+”和“=”字符
  6. LEFT 从 (5) 中获取前 8 个字符,如果您的随机字符串中需要更多或更少的字符,请将 8 更改为其他字符
  7. LPAD如果长度小于 8 个字符,则在 (6) 的开头插入零;再次,如果需要,将 8 更改为其他内容


Nik*_* G. 6

要生成随机字符串,可以使用:

SUBSTRING(MD5(RAND()) FROM 1 FOR 8)

您收到这样的信息:

353E50CC


小智 6

这是我的工作,生成 6 位数字并在 MySQL 中更新:

产生:

SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 6)
Run Code Online (Sandbox Code Playgroud)

更新:

UPDATE table_name 
SET column_name = SUBSTRING(MD5(RAND()) FROM 1 FOR 6) 
WHERE id = x12
Run Code Online (Sandbox Code Playgroud)


Ric*_*omo 5

I 使用另一列中的数据生成“散列”或唯一字符串

UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )
Run Code Online (Sandbox Code Playgroud)


TV-*_*-15 5

字母表中的 8 个字母 - 全部大写:

UPDATE `tablename` SET `tablename`.`randomstring`= concat(CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25)))CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))));
Run Code Online (Sandbox Code Playgroud)