如何为我的一个MySql表列生成唯一的随机字符串?

时间:2016-08-31 19:16:24

标签: mysql random sql-update constraints unique

我正在使用MySql 5.5.37。我有一个包含以下列的表

+------------------+------------------+------+-----+---------+-------+
| Field            | Type             | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| ID               | varchar(32)      | NO   | PRI | NULL    |       |
| CODE             | varchar(6)       | NO   | UNI | NULL    |       |

代码列是唯一的,我的ID列是GUID。根据上表中的某些条件(例如WHERE COLUMN1 = 0),我想要更新多行。如何为我的CODE列生成随机的,唯一的6个字符的代码(理想情况是字母和数字),以便它们不违反我表中的唯一约束?请注意,表中不符合条件的列(例如,其中COLUMN1<> 0)已经具有CODE列的唯一值。

修改:这与此问题不同 - Generating a random & unique 8 character string using MySQL,因为该链接处理ID为taht是数字。我的ID是32个字符的字符串。此外,他们的解决方案没有考虑在运行我想要运行的语句之前表中可能有值的事实,这将为相关列生成唯一值。

6 个答案:

答案 0 :(得分:18)

BEFORE UPDATE触发器解决方案:

您可以使用:

创建一个6个字符的随机字母数字大写字符串
lpad(conv(floor(rand()*pow(36,6)), 10, 36), 6, 0);

为了不创建现有字符串,您可以使用BEFORE UPDATE触发器。

DELIMITER //
CREATE TRIGGER `unique_codes_before_update`
BEFORE UPDATE ON `unique_codes` FOR EACH ROW 
BEGIN
    declare ready int default 0;
    declare rnd_str text;
    if new.CODE is null then
        while not ready do
            set rnd_str := lpad(conv(floor(rand()*pow(36,6)), 10, 36), 6, 0);
            if not exists (select * from unique_codes where CODE = rnd_str) then
                set new.CODE = rnd_str;
                set ready := 1;
            end if;
        end while;
    end if;
END//
DELIMITER ;

每次在CODE语句中将NULL列设置为UPDATE时,触发器都会在循环中创建一个新的随机字符串,直到在表中找不到匹配项为止。

现在您可以将所有NULL值替换为:

update unique_codes set CODE = NULL where code is NULL;

SQLFiddle demo here我使用一个字符随机字符串来证明没有值重复。

您还可以在BEFORE INSERT触发器中使用相同的代码。这样,您只需插入CODE=NULL的新行,触发器就会将其设置为新的唯一随机字符串。而且你永远不需要再次更新它。

原始答案(32个字符串):

select lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0) as rnd_str_8;

-- output example: 3AHX44TF

将生成一个8个字符的字母数字大写随机字符串。连接其中四个以获得32个字符:

select concat(
    lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0),
    lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0),
    lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0),
    lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0)
) as rnd_str_32;

-- output example: KGC8A8EGKE7E4MGD4M09U9YWXVF6VDDS

http://sqlfiddle.com/#!9/9eecb7d/76933

那么uniqness呢?好吧 - 尝试生成重复项; - )

答案 1 :(得分:1)

CONV(CONV(( SELECT MAX(CODE) FROM tbl ), 36, 10) + 1, 10, 36)

会为您提供下一个号码'以base-36(数字和大写字母)编码。

例如:

SELECT CONV(CONV(( 'A1B2C' ), 36, 10) + 1, 10, 36); --> 'A1B2D'

答案 2 :(得分:0)

这个很棘手,但我认为我已经找到了一个很好的解决方案:

DROP FUNCTION IF EXISTS getRandomAlphaNumeric;

DELIMITER $$

CREATE FUNCTION getRandomAlphaNumeric() RETURNS CHAR(6)
DETERMINISTIC
BEGIN

    SELECT 
    CONCAT (
        CHAR(FLOOR(RAND()*10)+48), CHAR(FLOOR(RAND()*26)+65), CHAR(FLOOR(RAND()*26)+97),
        CHAR(FLOOR(RAND()*10)+48), CHAR(FLOOR(RAND()*26)+65), CHAR(FLOOR(RAND()*26)+97)
    ) INTO @code
    ;

    RETURN @code;
END
$$

DELIMITER ;


DROP PROCEDURE IF EXISTS generateCodes;

DELIMITER $$
CREATE PROCEDURE generateCodes()
BEGIN

    SET @count = 0;
    SELECT COUNT(1) INTO @count FROM demo.codes;

    SET @i = 0;
    WHILE @i < @count DO

        PREPARE stmt FROM "SELECT @id := id, @itemCode := code FROM demo.codes p LIMIT ?, 1;";
        EXECUTE stmt USING @i;

        SET @code = getRandomAlphaNumeric();

        SELECT COUNT(1) INTO @countRowsWithCode FROM demo.codes WHERE code = @code;

        IF @countRowsWithCode = 0 AND @itemCode IS NULL THEN
            UPDATE demo.codes SET code = @code WHERE id = @id;
        END IF;

        SET @i := @i + 1;   
    END WHILE;
END
$$

DELIMITER ;


CALL generateCodes();

首先,我创建了一个函数,该函数返回一个由6个字符组成的随机字符串,用于生成所需的代码:

DROP FUNCTION IF EXISTS getRandomAlphaNumeric;

DELIMITER $$

CREATE FUNCTION getRandomAlphaNumeric() RETURNS CHAR(6)
DETERMINISTIC
BEGIN

    SELECT 
    CONCAT (
        CHAR(FLOOR(RAND()*10)+48), CHAR(FLOOR(RAND()*26)+65), CHAR(FLOOR(RAND()*26)+97),
        CHAR(FLOOR(RAND()*10)+48), CHAR(FLOOR(RAND()*26)+65), CHAR(FLOOR(RAND()*26)+97)
    ) INTO @code
    ;

    RETURN @code;
END
$$

然后我创建了一个程序,负责使用随机唯一代码更新表。 该程序包括:

  • 计算将使用6个字符的新鲜随机代码更新的所有记录。

    SELECT COUNT(1) INTO @count FROM demo.codes;

  • 然后,foreach行(使用WHILE循环):

    • 获取要提升的下一条记录的ID

      PREPARE stmt FROM "SELECT @id := id, @itemCode := code FROM demo.codes p LIMIT ?, 1;"; EXECUTE stmt USING @i;

    • 获取记录的新代码:

      SET @code = getRandomAlphaNumeric();

    • 最后,验证表格中是否已存在新代码,以及当前字段列是否没有值(NULL,如果&#39;不是,用随机代码更新当前记录:

      SELECT COUNT(1) INTO @countRowsWithCode FROM demo.codes WHERE code = @code; IF @countRowsWithCode = 0 AND @itemCode IS NULL THEN UPDATE demo.codes SET code = @code WHERE id = @id; END IF;

    • 最后,CALL创建的PROCEDURE,以填充code列中NULL列的字段。

      CALL generateCodes();

答案 3 :(得分:0)

尝试使用代码

SELECT LEFT(MD5(NOW()), 6) AS CODE;

LEFT(MD5(NOW()), 6)这将返回包含6个字符的唯一代码。

尝试其他方式

SELECT LEFT(UUID(), 6);

LEFT(UUID(), 6)这也将返回唯一代码

答案 4 :(得分:0)

DELIMITER $$

USE `db` $$

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')

每次都会给你一个唯一的8个字符串。

答案 5 :(得分:0)

简单有效的解决方案,以获取随机的10个字符的字符串,该字符串具有大写和小写字母和数字,而无需自定义功能(适用于MySQL> = 5.6):

select substring(base64_encode(md5(rand())) from 1+rand()*4 for 10);