Создание уникальных 10-символьных буквенно-цифровых хэшей в MySQL

У меня есть простая таблица с полем под названием "hash" VARCHAR 10 UNIQUE FIELD

Теперь я хотел бы запустить запрос и автоматически генерировать хэши внутри поля.

Проблема в том, что хэши должны быть буквенно-цифровыми, иметь длину 10 символов и быть УНИКАЛЬНЫМИ.

структура таблицы:

CREATE TABLE `vouchers` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `hash` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `hash` (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Поэтому мне нужно ВСТАВИТЬ хэши в хеш-поле, они должны выглядеть как случайные буквенно-цифровые случайные хэши, я имею в виду, что пользователи не должны иметь возможность поймать следующий или предыдущий хэш, просто глядя на один хэш, также они должны быть длиной 10 символов и уникальными.

Кто-нибудь знает об этом?


person itsme    schedule 31.08.2013    source источник
comment
MySQL поддерживает только автоматическое увеличение целых чисел, и выбор в varchar также всегда медленнее, чем в столбце int. Я предполагаю, что у вас есть очень веская причина, почему вы хотите это сделать?   -  person Raymond Nijland    schedule 31.08.2013
comment
@RaymondNijland да, чтобы не создавать скрипт со стороны приложения :)   -  person itsme    schedule 31.08.2013


Ответы (5)


Если вы хотите создать уникальные значения для этого поля, вы можете использовать подход с автоматическим приращением, просто с основанием 36. Вот пример, в котором количество различных значений достигает нескольких сотен миллионов:

update t cross join (select @i := 0, @chars = '0123456789abcdefghijklmnopqrstuvwxyz') const
    set hash = concat(substring(@chars, ((@i := @i + 1) %36)+1, 1),
                      substring(@chars, floor(@i/pow(36, 1))%36 + 1, 1),
                      substring(@chars, floor(@i/pow(36, 2))%36 + 1, 1),
                      substring(@chars, floor(@i/pow(36, 3))%36 + 1, 1),
                      substring(@chars, floor(@i/pow(36, 4))%36 + 1, 1),
                      substring(@chars, floor(@i/pow(36, 5))%36 + 1, 1),
                      '0000'
                     );

РЕДАКТИРОВАТЬ: (на основе пересмотренного вопроса)

Ваша таблица имеет уникальное ограничение. Я бы просто сделал следующее:

insert into vouchers(hash)
    select concat(substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1),
                  substring(@chars, floor(rand()*36) + 1, 1)
                 );

Просто сделайте это несколько раз в цикле (или по мере необходимости), чтобы заполнить таблицу. Маловероятно, что вы получите дубликаты. Если вы это сделаете, эта конкретная вставка потерпит неудачу.

person Gordon Linoff    schedule 31.08.2013
comment
спасибо, хорошо, я проверяю это, но возвращает ошибку: [ОШИБКА в запросе 2] Каждая производная таблица должна иметь свой собственный псевдоним - person itsme; 31.08.2013
comment
Вы можете обернуть это в функцию mysql для более простых результатов. - person zevra0; 31.08.2013
comment
@sbaaaang . . . Я проверил логику, но в операторе select, а не в обновлении. Я просто добавил const к назначению @i и переместил chars в тот же подзапрос. Это исключает отдельную команду set. - person Gordon Linoff; 31.08.2013
comment
@ zevra0 а? я новичок в функциях mysql.. :( - person itsme; 31.08.2013
comment
@GordonLinoff теперь ошибок нет, но я не могу вставить хэши, таблица пуста: P - person itsme; 31.08.2013
comment
@sbaaaang . . . Возможно, я неправильно понял вопрос. Вы пытаетесь обновить существующую таблицу или пытаетесь вставить новые строки в новую таблицу? - person Gordon Linoff; 31.08.2013
comment
Таблица @GordonLinoff пуста, мне нужно вставить их, чтобы не обновлять;) - person itsme; 31.08.2013
comment
@sbaaaang . . . Измените свой вопрос, указав формат таблицы и количество значений, которые вы хотите создать. - person Gordon Linoff; 31.08.2013
comment
@GordonLinoff теперь лучше? :П - person itsme; 31.08.2013
comment
функция создания hash10() возвращает varchar(10) начало объявления символов varchar(34); установить символы = '0123456789abcdefghijklmnopqrstuvwxyz'; return concat(substring(chars, floor(rand()*36) + 1, 1), substring(chars, floor(rand()*36) + 1, 1), substring(chars, floor(rand()*36) ) + 1, 1), подстрока (символы, пол (rand () * 36) + 1, 1), подстрока (символы, пол (rand () * 36) + 1, 1) ); конец| разделитель ; - person zevra0; 31.08.2013
comment
Извините, пытался опубликовать код... в основном просто оберните вызов Гордона выше в функцию mysql... создайте функцию hash10(). Таким образом, вы можете упростить вызов для вставки в таблицу X значений (hash10()), (hash10()) и т. д. - person zevra0; 31.08.2013

-- most elegant, has adjustable length 1-32 and probably has best performance
SELECT SUBSTR(REPLACE(UUID(),'-',''),1,10) as randomStringUUID
;

-- generate 10 character [a-z0-9] string, has adjustable letter/nr ratio
SELECT CONCAT(
  CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ,CASE WHEN RAND()>=0.5 THEN char(round(RAND()*9+48)) ELSE char(round(RAND()*25+97)) END
  ) as randomString
;

-- as bonus: generate a variable size letter only string, best for emulating names/words
SELECT SUBSTR(CONCAT(char(RAND()*25+55),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97),char(RAND()*25+97)),1,RAND()*9+4) as RandomName

Протестируйте на http://sqlfiddle.com/#!8/d41d8/586.

person Stefan Rogin    schedule 10.09.2014

Вот код, чтобы обернуть ответ Гордона выше в функцию (кредит Гордону) -

delimiter |
create function hash10() returns varchar(10)
begin
declare chars varchar(36);
set chars = '0123456789abcdefghijklmnopqrstuvwxyz';
return concat(substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1),
              substring(chars, floor(rand()*36) + 1, 1)
             );
end|
delimiter ;

Тогда вы можете использовать...

insert into x (hash) values (hash10()),(hash10()),(hash10());
person zevra0    schedule 31.08.2013
comment
Должно быть: declare chars varchar(36); - person wieczorek1990; 13.11.2015

Я думаю, что лучше справиться с этим из логики приложения.

Если вы хотите справиться с этим с помощью sql, попробуйте использовать функцию mysql UUID() (но сгенерированный uuid имеет длину 36 символов)

person manuskc    schedule 31.08.2013
comment
действительно, вы должны справиться с этим из приложения, но это будет намного сложнее. - person Raymond Nijland; 31.08.2013
comment
@Manu, я согласен, просто я ленивый, я хотел бы знать, есть ли здесь способ избежать написания сценария приложения - person itsme; 31.08.2013

Просто используйте цикл:

DROP FUNCTION hash10;
DELIMITER |
CREATE FUNCTION hash10() RETURNS VARCHAR(10)
BEGIN
  DECLARE chars VARCHAR(36);
  DECLARE result VARCHAR(10);
  DECLARE i INT;
  SET chars = '0123456789abcdefghijklmnopqrstuvwxyz';
  SET result = '';
  SET i = 0;
  label: LOOP
    SET result = CONCAT(result, SUBSTRING(chars, FLOOR(RAND()*36) + 1, 1));
    SET i = i + 1;
    IF i = 10 THEN
      LEAVE label;
    END IF;
  END LOOP label;
  RETURN result;
END|
DELIMITER ;

Чтобы сгенерировать другую длину, просто замените все 10 на другое число.

person wieczorek1990    schedule 13.11.2015