Подтвердить что ты не робот

Создание случайной и уникальной 8-символьной строки с использованием MySQL

Я работаю над игрой, которая в какой-то момент включает в себя транспортные средства. У меня есть таблица MySQL с именем "транспортные средства", содержащая данные о транспортных средствах, включая колонку "тарелка", в которой хранятся лицензионные знаки для транспортных средств.

Теперь вот часть, с которой у меня проблемы. Мне нужно найти неиспользуемый номерной знак перед созданием нового автомобиля - это должна быть буквенно-цифровая 8- char случайная строка. Как я это достиг, это использование цикла while в Lua, который является языком, на котором я программирую, для генерации строк и запроса БД, чтобы узнать, используется ли он. Однако, по мере увеличения количества автомобилей, я ожидаю, что это станет еще более неэффективным, чем сейчас. Поэтому я решил попробовать эту проблему, используя запрос MySQL.

Необходимый запрос должен просто сгенерировать 8-значную буквенно-цифровую строку, которая еще не указана в таблице. Я снова думал о подходе к генерации & check loop, но я не ограничиваю этот вопрос тем, что есть более эффективный. Я смог генерировать строки, определяя строку, содержащую все разрешенные символы и беспорядочно подстроки, и ничего больше.

Любая помощь приветствуется.

4b9b3361

Ответ 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();

Ответ 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')