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

to94eoyn  于 2021-06-18  发布在  Mysql
关注(0)|答案(22)|浏览(451)

我正在做一个游戏,在某个时候涉及到车辆。我有一个名为“vehicles”的mysql表,其中包含有关车辆的数据,包括存储车辆牌照的列“plate”。
现在我遇到的问题来了。我需要找到一个未使用的车牌,然后再创建一个新的车辆-它应该是一个字母数字8字符随机字符串。我是如何做到这一点的,是使用lua中的while循环生成字符串并查询db以查看是否使用了它。然而,随着车辆数量的增加,我预计这将变得更加低效,它是现在。因此,我决定尝试使用mysql查询来解决这个问题。
我需要的查询应该只是生成一个表中还没有的8个字符的字母数字字符串。我又想到了generate&check循环方法,但我并没有把这个问题局限于此,以防有更有效的方法。我已经能够通过定义一个包含所有允许的字符的字符串并随机对其进行子串来生成字符串,仅此而已。
感谢您的帮助。

zvokhttg

zvokhttg1#

我正在寻找类似的东西,我决定制作自己的版本,如果需要的话,也可以指定一个不同的种子(字符列表)作为参数:

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')
vzgqcmou

vzgqcmou2#

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

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演示)
如果列允许空值,则可以这样做。如果希望它不为null,则需要定义一个默认值

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

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

fykwrbwg

fykwrbwg3#

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

yb3bgrhw4#

这个问题由两个完全不同的子问题组成:
字符串必须看起来是随机的
字符串必须是唯一的
虽然随机性很容易实现,但没有重试循环的唯一性却不是。这就使我们首先集中注意独特性。非随机唯一性可以通过 AUTO_INCREMENT . 所以使用保持唯一性的伪随机变换就可以了:
hash是由@paul建议的
aes加密也适合
但是有一个很好的例子: RAND(N) 它自己!
由同一种子创建的随机数序列保证
可复制的
前8次迭代不同
如果种子是 INT32 所以我们使用了@andreyvolk或@gordonlinoff的方法,但是有一个种子 RAND :
e、 g.假设 id 是一个 AUTO_INCREMENT 列:

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;
gj3fmq9x

gj3fmq9x5#

如何计算连续整数的md5(或其他)散列,然后取前8个字符。

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

等。
警告:我不知道在发生冲突之前可以分配多少(但这是一个已知的常量值)。
编辑:这是一个古老的答案,但随着时间的推移,我又看到了它,所以,从观察中。。。
所有数字的概率=2.35%
所有字母的概率=0.05%
当md5(82945)=“7b763dcb…”时发生第一次碰撞(与md5(25302)的结果相同)

hxzsmxv2

hxzsmxv26#

简单有效的解决方案,可获得随机的10个字符的字符串,其中包含大小写字母和数字:

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

inn6fuwd7#

对于“生成随机字符串”,可以使用: SUBSTRING(MD5(RAND()) FROM 1 FOR 8) 你收到的短信是这样的: 353E50CC

lymnna71

lymnna718#

如果你对“随机”但完全可预测的车牌号没问题,你可以使用线性反馈移位寄存器来选择下一个车牌号——它保证在重复之前遍历每个号码。然而,如果没有一些复杂的数学,你将无法通过每8个字符的字母数字字符串(你将得到2^41的36^8(78%)可能板)。为了让它更好地填满你的空间,你可以从盘子里去掉一个字母(也许是o),这样你就可以得到97%。

a7qyws3x

a7qyws3x9#

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

pobjuy3210#

生成8个字符的密钥

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

如何为mysql表中的一列生成唯一的随机字符串?

6jygbczu

6jygbczu11#

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')
w8f9ii69

w8f9ii6912#

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

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;

注:不保证唯一性。你得单独检查一下。

rhfm7lfc

rhfm7lfc13#

生成唯一数字的简单方法

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

ubbxdtey14#

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

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

由内而外解释: RAND 生成一个介于0和1之间的随机数 MD5 计算a-f和0-9中32个字符(1)的md5和 UNHEX 将(2)转换为16字节,值从00到ff TO_BASE64 将(3)编码为base64,从a-z和a-z到0-9加上“/”和“+”,后跟两个“=”
三个 REPLACE s从(4)中删除“/”、“+”和“=”字符 LEFT 取(5)中的前8个字符,如果需要在随机字符串中增加或减少字符,请将8改为其他字符 LPAD 如果长度小于8个字符,则在(6)开头插入零;同样,如果需要的话,把8换成别的

mepcadol

mepcadol15#

如果您没有id或种子,如insert中的值列表:

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

相关问题