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

时间:2013-05-24 14:55:10

标签: mysql sql

我正在制作一款涉及车辆的游戏。我有一个名为“车辆”的MySQL表,其中包含有关车辆的数据,包括存储车辆牌照的“板”栏。

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

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

感谢任何帮助。

22 个答案:

答案 0 :(得分:75)

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

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

SELECT LEFT(UUID(), 8);

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

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

答案 1 :(得分:71)

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

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

虽然很容易实现随机性,但没有重试循环的唯一性则不然。这使我们首先专注于独特性。使用AUTO_INCREMENT可以轻松实现非随机唯一性。因此,使用唯一性保留,伪随机变换就可以了:

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

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

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

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

e.g。 Assumin id是一个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;

答案 2 :(得分:49)

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

MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e

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

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

所有数字的机会= 2.35%

所有字母的机会= 0.05%

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

答案 3 :(得分:25)

创建随机字符串

这是一个用于创建给定长度的随机字符串的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

用法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 ;

答案 4 :(得分: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;

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

答案 5 :(得分:16)

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

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

答案 6 :(得分:13)

您可以使用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;

答案 7 :(得分:12)

您可以使用以下内容生成随机字母数字字符串

lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0);

您可以在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 ;

现在只需插入您的数据

insert into vehicles(col1, col2) values ('value1', 'value2');

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

sqlfiddle demo

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

`plate` CHAR(8) NOT NULL DEFAULT 'default',

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

答案 8 :(得分:3)

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

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

你这样接受了这样的事情:

353E50CC

答案 9 :(得分:3)

我使用其他列中的数据生成&#34;哈希&#34;或唯一的字符串

UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )

答案 10 :(得分:3)

字母表中的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))));

答案 11 :(得分:2)

简单有效的解决方案,以获取随机的10个字符的字符串,包括大小写字母和数字:

select substring(base64_encode(md5(rand())) from 1+rand()*4 for 10);

答案 12 :(得分:2)

如果你没有id或种子,就像它在insert中的值列表一样:

REPLACE(RAND(), '.', '')

答案 13 :(得分:2)

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

LPAD(LEFT(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(RAND()))), "/", ""), "+", ""), "=", ""), 8), 8, 0)

由内而外解释:

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

答案 14 :(得分:1)

如果您使用&#34;随机&#34;但是完全可以预测的牌照,您可以使用linear-feedback shift register来选择下一个牌号 - 它可以保证在重复之前通过每个号码。但是,如果没有一些复杂的数学运算,您将无法通过每8个字符的字母数字字符串(您可以从36 ^ 8(78%)个可能的板中获得2 ^ 41个)。为了让你的空间更好,你可以从盘子中排除一个字母(也许是O),给你97%。

答案 15 :(得分:1)

要创建随机的 10位字母数字,不包括相似字符01oOlI:

LPAD(LEFT(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(RAND()))), "/", ""), "+", ""), "=", ""), "O", ""), "l", ""), "I", ""), "1", ""), "0", ""), "o", ""), 10), 10, 0)

这正是我创建优惠券代码所需要的。消除了混乱的字符,以减少在将其输入凭证代码形式时出错。

希望这对某人有所帮助,基于Jan Uhlig's brilliant answer

有关该代码的工作方式,请参阅Jan的答案。

答案 16 :(得分:1)

考虑到您需要的字符总数,您生成两个完全相似的牌照的可能性非常小。因此,你可能会在LUA中生成数字。

你有36 ^ 8个不同的独特数字牌(2,821,109,907,456,这很多),即使你已经拥有一百万个牌照,你生成一个已经拥有的牌照的机率非常小,约为0.000035%

当然,这一切都取决于你最终会创建多少个牌照。

答案 17 :(得分:0)

DELIMITER $$

USE `temp` $$

DROP PROCEDURE IF EXISTS `GenerateUniqueValue`$$

CREATE PROCEDURE `GenerateUniqueValue`(IN tableName VARCHAR(255),IN columnName VARCHAR(255)) 
BEGIN
    DECLARE uniqueValue VARCHAR(8) DEFAULT "";
    WHILE LENGTH(uniqueValue) = 0 DO
        SELECT CONCAT(SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1)
                ) INTO @newUniqueValue;
        SET @rcount = -1;
        SET @query=CONCAT('SELECT COUNT(*) INTO @rcount FROM  ',tableName,' WHERE ',columnName,'  like ''',@newUniqueValue,'''');
        PREPARE stmt FROM  @query;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    IF @rcount = 0 THEN
            SET uniqueValue = @newUniqueValue ;
        END IF ;
    END WHILE ;
    SELECT uniqueValue;
    END$$

DELIMITER ;

使用此存储过程并每次都使用它

Call GenerateUniqueValue('tableName','columnName')

答案 18 :(得分:0)

一种生成唯一编号的简便方法

set @i = 0;
update vehicles set plate = CONCAT(@i:=@i+1, ROUND(RAND() * 1000)) 
order by rand();

答案 19 :(得分:0)

此函数根据您输入的长度和允许的字符生成随机字符串,如下所示:

SELECT str_rand(8, '23456789abcdefghijkmnpqrstuvwxyz');

功能代码:

DROP FUNCTION IF EXISTS str_rand;

DELIMITER //

CREATE FUNCTION str_rand(
    u_count INT UNSIGNED,
    v_chars TEXT
)
RETURNS TEXT
NOT DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
COMMENT ''
BEGIN
    DECLARE v_retval TEXT DEFAULT '';
    DECLARE u_pos    INT UNSIGNED;
    DECLARE u        INT UNSIGNED;

    SET u = LENGTH(v_chars);
    WHILE u_count > 0
    DO
      SET u_pos = 1 + FLOOR(RAND() * u);
      SET v_retval = CONCAT(v_retval, MID(v_chars, u_pos, 1));
      SET u_count = u_count - 1;
    END WHILE;

    RETURN v_retval;
END;
//
DELIMITER ;

此代码基于“罗斯史密斯二世”发送的shuffle string function

答案 20 :(得分:0)

生成8个字符的密钥

lpad(conv(floor(rand()*pow(36,6)), 10, 36), 8, 0); 
  

How do I generate a unique, random string for one of my MySql table columns?

答案 21 :(得分:0)

我一直在寻找相似的东西,所以我决定制作自己的版本,如果需要,您还可以指定其他种子(字符列表)作为参数:

CREATE FUNCTION `random_string`(length SMALLINT(3), seed VARCHAR(255)) RETURNS varchar(255) CHARSET utf8
    NO SQL
BEGIN
    SET @output = '';

    IF seed IS NULL OR seed = '' THEN SET seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; END IF;

    SET @rnd_multiplier = LENGTH(seed);

    WHILE LENGTH(@output) < length DO
        # Select random character and add to output
        SET @output = CONCAT(@output, SUBSTRING(seed, RAND() * (@rnd_multiplier + 1), 1));
    END WHILE;

    RETURN @output;
END

可以用作:

SELECT random_string(10, '')

将使用内置的大小写字符+数字种子。 NULL也将是值,而不是“。”。

但是可以在调用时指定自定义种子:

SELECT random_string(10, '1234')