【问题标题】:Generate unique 10 chars alphanumeric hashes in MySQL在 MySQL 中生成唯一的 10 个字符的字母数字哈希
【发布时间】:2013-08-31 13:07:32
【问题描述】:

我有一个名为"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 个字符长并且独一无二。

有人知道吗?

【问题讨论】:

  • MySQL 仅支持自动递增 int,并且 varchar 上的选择也总是比 int 列慢。我想你有一个很好的理由为什么要这样做?
  • @RaymondNijland 是的,以避免从应用程序端创建脚本:)

标签: mysql unique dynamically-generated hash


【解决方案1】:
-- 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测试

【讨论】:

    【解决方案2】:

    这是将上面 Gordon 的答案包装成一个函数的代码(归功于 Gordon)-

    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());
    

    【讨论】:

    • 应该是:declare chars varchar(36);
    【解决方案3】:

    如果您想为该字段创建唯一值,可以使用自动递增方法,以 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)
                     );
    

    只需在循环中(或根据需要)多次执行此操作即可填充表格。您极不可能得到重复项。如果这样做,该特定插入将失败。

    【讨论】:

    • 谢谢,很好,我正在测试它,但它返回错误:[查询 2 中的错误] 每个派生表都必须有自己的别名
    • 您可以将其包装在 mysql 函数中以获得更简单的结果。
    • @sbaaaang 。 . .我测试了逻辑,但在select 语句中,而不是更新。我刚刚将const 添加到@i 的分配中,并将chars 移动到同一个子查询中。这消除了单独的set 命令。
    • @zevra0 嗯?我是 mysql 函数的新手.. :(
    • @GordonLinoff 现在没有错误,但我无法插入哈希,表是空的:P
    【解决方案4】:

    我认为最好从应用程序逻辑中处理。

    如果你想用sql方式处理,试试mysql函数UUID()(但是生成的uuid是36个字符)

    【讨论】:

    • 确实你应该从应用程序中处理它,但它会更难实现。
    • @Manu 我同意只是我很懒我想知道这里是否有某种方法可以避免编写应用程序脚本
    【解决方案5】:

    只需使用循环:

    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 替换为不同的数字即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-31
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 2019-05-17
      • 2017-12-09
      • 2011-08-18
      • 2021-01-12
      相关资源
      最近更新 更多