高大的洋葱 · MySQL操作之存储过程开发者社区· 2 周前 · |
爱看球的太阳 · 将名为MOD11C3.A2018001.06 ...· 3 月前 · |
气势凌人的仙人掌 · linux下查看opencv安装路径以及版本 ...· 7 月前 · |
失望的日光灯 · Spring populateBean属性赋 ...· 9 月前 · |
追风的小笼包 · 「Learn」开发记录-腾讯云开发者社区-腾讯云· 1 年前 · |
傻傻的大脸猫 · udp的sendto报错“network ...· 1 年前 · |
我正在开发一款游戏,在某些时候涉及到车辆。我有一个名为" vehicles“的MySQL表,其中包含有关车辆的数据,其中包括存储车辆牌照的列”vehicles“。
现在我遇到问题的部分来了。我需要在创建新车之前找到一个未使用的车牌-它应该是一个由8个字符组成的字母数字随机字符串。我是通过在Lua中使用while循环来生成字符串并查询DB以查看是否使用了它,这是我正在编程的语言。然而,随着车辆数量的增加,我预计这将变得更加低效。因此,我决定尝试使用MySQL查询来解决这个问题。
我需要的查询应该只是生成一个8个字符的字母数字字符串,该字符串还不在表中。我再次想到了生成和检查循环的方法,但我不会将这个问题限制在这个范围内,以防有更有效的方法。我已经能够通过定义一个包含所有允许的字符的字符串来生成字符串,并随机地对其进行子化,仅此而已。
任何帮助都是非常感谢的。
如何计算连续整数的MD5 (或其他)散列,然后获取前8个字符。
i.e
MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e
等。
警告:我不知道在冲突之前你可以分配多少(但它将是一个已知的常量值)。
编辑:这是一个旧的答案,但随着时间的推移,我又看到了它,所以,从观察来看…
所有数字的概率= 2.35%
所有字母的概率= 0.05%
当MD5(82945) = "7b763dcb...“时第一次冲突(结果与MD5(25302)相同)
以下是一种使用字母数字作为有效字符的方法:
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;
注意:不能保证唯一性。你得单独检查一下。
考虑到所需的字符总数,生成两个完全相同的车牌的可能性非常小。因此,您可能不需要在LUA中生成数字。
你有36^8个不同的唯一车牌(2,821,109,907,456,这是很多),即使你已经有一百万个车牌,你也只有很小的机会生成一个你已经有的车牌,大约0.000035%
当然,这完全取决于你最终会创建多少个车牌。
我不关心碰撞的可能性。只需生成一个随机字符串并检查它是否存在。如果是这样,再试一次,除非你已经分配了大量的车牌,否则你不需要做更多的次数。
用纯(我的)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
由于这篇文章受到了意想不到的关注,让我强调一下 ADTC's comment :上面的代码非常愚蠢,并产生了连续的数字。
对于稍微不那么愚蠢的随机性,可以试着这样做:
SELECT LEFT(MD5(RAND()), 8)
对于真正的(加密安全的)随机性,使用
RANDOM_BYTES()
而不是
RAND()
(但是我会考虑将此逻辑上移到应用层)。
如果你对“随机”但完全可预测的车牌没有意见,你可以使用 linear-feedback shift register 来选择下一个车牌号码-它保证在重复之前检查每个数字。但是,如果没有一些复杂的数学运算,您将无法遍历每个8个字符的字母数字字符串(您将获得36^8 (78%)个可能牌照中的2^41个)。为了更好地填充您的空间,您可以从车牌中排除一个字母(可能是O),得到97%。
您可以使用以下命令生成随机字母数字字符串:
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 )
如果该列允许空值,则可以这样工作。如果希望它不为空,则需要定义一个缺省值
`plate` CHAR(8) NOT NULL DEFAULT 'default',
如果大写字母数字不是您想要的,您也可以在触发器中使用任何其他随机字符串生成算法。但是触发器将照顾到唯一性。
我使用另一列中的数据来生成"hash“或唯一字符串
UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )
字母表中的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))));
如果您没有id或种子,就像它在insert中的值列表一样:
REPLACE(RAND(), '.', '')
下面是生成随机字符串的另一种方法:
SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 8) AS myrandomstring
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')
对于生成随机字符串,您可以使用:
SUBSTRING(MD5(RAND()) FROM 1 FOR 8)
你会收到这样的smth:
353E50CC
一种生成唯一数字的简单方法
set @i = 0;
update vehicles set plate = CONCAT(@i:=@i+1, ROUND(RAND() * 1000))
order by rand();
此函数根据您的输入长度和允许的字符生成随机字符串,如下所示:
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
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;
DELIMITER ;
这个代码是基于“罗斯·史密斯二世”发送的 shuffle string function
我正在寻找类似的东西,我决定制作自己的版本,如果需要,还可以指定不同的种子(字符列表)作为参数:
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也可以是value而不是'‘。
但可以在调用时指定自定义种子:
SELECT random_string(10, '1234')
简单而有效的解决方案,以获取包含大小写字母和数字的随机10个字符的字符串:
select substring(base64_encode(md5(rand())) from 1+rand()*4 for 10);
UPPER(HEX(UUID_SHORT()))
为您提供唯一的16个字符的字母数字字符串。它有一些不太可能的警告,请参阅 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_uuid-short
"next“值通常是可预测的:
mysql> SELECT UPPER(HEX(UUID_SHORT()));
+--------------------------+
| UPPER(HEX(UUID_SHORT())) |
+--------------------------+
| 161AA3FA5000006 |
+--------------------------+
mysql> SELECT UPPER(HEX(UUID_SHORT()));
+--------------------------+
| UPPER(HEX(UUID_SHORT())) |
+--------------------------+
| 161AA3FA5000007 |
+--------------------------+
转换为BASE64可以将字符串缩小到11个字符:
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_to-base64
mysql> SELECT TO_BASE64(UNHEX(HEX(UUID_SHORT())));
+-------------------------------------+