Создание случайной и уникальной 8-символьной строки с использованием MySQL
Я работаю над игрой, которая в какой-то момент включает в себя транспортные средства. У меня есть таблица MySQL с именем "транспортные средства", содержащая данные о транспортных средствах, включая колонку "тарелка", в которой хранятся лицензионные знаки для транспортных средств.
Теперь вот часть, с которой у меня проблемы. Мне нужно найти неиспользуемый номерной знак перед созданием нового автомобиля - это должна быть буквенно-цифровая 8- char случайная строка. Как я это достиг, это использование цикла while в Lua, который является языком, на котором я программирую, для генерации строк и запроса БД, чтобы узнать, используется ли он. Однако, по мере увеличения количества автомобилей, я ожидаю, что это станет еще более неэффективным, чем сейчас. Поэтому я решил попробовать эту проблему, используя запрос MySQL.
Необходимый запрос должен просто сгенерировать 8-значную буквенно-цифровую строку, которая еще не указана в таблице. Я снова думал о подходе к генерации & check loop, но я не ограничиваю этот вопрос тем, что есть более эффективный. Я смог генерировать строки, определяя строку, содержащую все разрешенные символы и беспорядочно подстроки, и ничего больше.
Любая помощь приветствуется.
Ответы
Ответ 1
Эта проблема состоит из двух очень разных подзадач:
- строка должна казаться случайной
- строка должна быть уникальной
В то время как случайность довольно легко достигается, уникальность без повторной циклы - нет. Это заставляет нас сначала сосредоточиться на уникальности. Неслучайная уникальность может быть достигнута тривиально с помощью AUTO_INCREMENT
. Таким образом, с помощью сохранения уникальности псевдослучайное преобразование было бы хорошо:
- @Paul
- предложил хэшAES-шифрование подходит также
- Но есть один хороший: сам TG41!
Последовательность случайных чисел, созданных одним и тем же начальным числом, гарантированно будет
- воспроизводимый
- отличается для первых 8 итераций
- если семя является
INT32
Поэтому мы используем подход @AndreyVolk или @GordonLinoff, но с затравленным RAND
:
например Assumin 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 [email protected];
Ответ 2
Как я сказал в своем комментарии, я не буду беспокоиться о вероятности столкновения. Просто сгенерируйте случайную строку и проверьте, существует ли она. Если это произойдет, попробуйте еще раз, и вам не нужно делать это больше, чем пару раз, если у вас уже не назначено огромное количество тарелок.
Другое решение для генерации 8-символьной длинной псевдослучайной строки в чистом (My) SQL:
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: приведенный выше фрагмент кода довольно глуп и выдает последовательные цифры.
Для чуть менее глупой случайности попробуйте что-то вроде этого:
SELECT LEFT(MD5(RAND()), 8)
А для истинной (криптографически безопасной) случайности используйте RANDOM_BYTES()
, а не RAND()
(но тогда я бы рассмотрел возможность перенести эту логику на уровень приложений).
Ответ 3
Как насчет вычисления MD5 (или другого) хэша последовательных целых чисел, а затем принимать первые 8 символов.
i.e
MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e
и др.
caveat: я не знаю, сколько вы могли бы выделить перед столкновением (но это было бы известное и постоянное значение).
edit: Это теперь старый ответ, но я видел его снова со временем на моих руках, поэтому, из наблюдения...
Шанс всех чисел = 2,35%
Шанс всех букв = 0,05%
Первое столкновение, когда MD5 (82945) = "7b763dcb..." (тот же результат, что и MD5 (25302))
Ответ 4
Создать случайную строку
Здесь используется функция 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 ;
Ответ 5
Вот один из способов, используя альфа-число как допустимые символы:
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;
Обратите внимание, что нет гарантии уникальности. Вы должны будете проверить это отдельно.
Ответ 6
Вот еще один метод генерации случайной строки:
SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 8) AS myrandomstring
Ответ 7
Вы можете использовать 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;
Ответ 8
Вы можете создать случайную буквенно-цифровую строку с помощью:
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. Если вы хотите, чтобы он был NOT NULL, вам нужно было бы определить значение по умолчанию
`plate` CHAR(8) NOT NULL DEFAULT 'default',
Вы также можете использовать любой другой алгоритм генерации произвольной строки в триггере, если буквенные буквы верхнего регистра - это не то, что вы хотите. Но триггер позаботится об уникальности.
Ответ 9
Для генерации случайной строки вы можете использовать:
SUBSTRING(MD5(RAND()) FROM 1 FOR 8)
Вы получаете что-то вроде этого:
353E50CC
Ответ 10
Я использую данные из другого столбца для генерации "хэш" или уникальной строки
UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )
Ответ 11
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))));
Ответ 12
Для строки, состоящей из 8 случайных чисел и букв верхнего и нижнего регистра, это мое решение:
LPAD(LEFT(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(RAND()))), "/", ""), "+", ""), "=", ""), 8), 8, 0)
Объясняется изнутри:
-
RAND
генерирует случайное число от 0 до 1
-
MD5
вычисляет сумму MD5 (1), 32 символа из a-f и 0-9
-
UNHEX
переводит (2) в 16 байтов со значениями от 00 до FF
-
TO_BASE64
кодирует (3) как base64, 22 символа из a-z и A-Z и 0-9 плюс "/" и "+", а затем два "="
- три
REPLACE
удаляют символы "/", "+" и "=" из (4)
-
LEFT
берет первые 8 символов из (5), меняет 8 на что-то другое, если вам нужно больше или меньше символов в вашей случайной строке
-
LPAD
вставляет нули в начале (6), если длина символа меньше 8 символов; снова, измените 8 на что-то еще при необходимости
Ответ 13
Если у вас нет идентификатора или семени, например его его для списка значений вставки:
REPLACE(RAND(), '.', '')
Ответ 14
Принимая во внимание общее количество символов, которые вам нужны, у вас будет очень небольшая вероятность создать две точно одинаковые номера. Таким образом, вы, вероятно, могли бы уйти от генерации чисел в LUA.
У вас есть 36 8 уникальных уникальных номеров (2821,109,907,456, что очень много), даже если у вас уже есть миллион номерных знаков, у вас будет очень мало шансов создать тот, который у вас уже есть, около 0,000035%
Конечно, все зависит от того, сколько числовых знаков вы создадите.
Ответ 15
Если вы в порядке со "случайными", но вполне предсказуемыми номерными знаками, вы можете использовать регистр сдвига с линейной обратной связью, чтобы выбрать следующий номер пластины - он должен пройти каждое число перед повторением. Однако, без какой-либо сложной математики, вы не сможете пройти через каждые 8 символов буквенно-цифровой строки (вы получите 2 ^ 41 из 36 ^ 8 (78%) возможных тарелок). Чтобы это лучше заполнило ваше пространство, вы можете исключить письмо из пластинок (возможно, O), давая вам 97%.
Ответ 16
Эта функция генерирует строку Random на основе введенной вами длины и разрешенных символов, например:
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 ;
Этот код основан на функции случайной строки, отправленной "Россом Смитом II"
Ответ 17
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
Простой способ генерировать уникальный номер
set @i = 0;
update vehicles set plate = CONCAT(@i:[email protected]+1, ROUND(RAND() * 1000))
order by rand();
Ответ 19
Сгенерировать ключ из 8 символов
lpad(conv(floor(rand()*pow(36,6)), 10, 36), 8, 0);
Как мне сгенерировать уникальную случайную строку для одного из моих столбцов таблицы MySql?
Ответ 20
Я искал что-то похожее, и я решил создать свою собственную версию, в которой вы также можете указать другое начальное число (список символов) в качестве параметра:
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, '')
Который будет использовать встроенное начальное число upper- и строчные буквы + цифры.
NULL также будет значением вместо ''.
Но можно указать пользовательское начальное число при вызове:
SELECT random_string(10, '1234')