mysql - 如何在 MySQL 上使用唯一的字符序列生成触发器?

标签 mysql triggers

o/

我想使用大写和小写 + 数字生成 4 个字符的序列..但我想仅使用在 MySQL 数据库上插入新用户之前的触发器生成此序列。

类似于=“Ae5f”或“5Bd2”

我正在使用

  CHAR( FLOOR(65 + (RAND() * 25))),
  CHAR( FLOOR(65 + (RAND() * 25))),
  CHAR( FLOOR(65 + (RAND() * 25))),
  CHAR( FLOOR(65 + (RAND() * 25)))

因此,代码将仅生成 4 位大写字母,并且不会是 UNIQUE 值,有时会返回“重复条目”。

有人可以帮助我吗?

------------------------ 编辑 --------------------------

我最后一次在 Base32 中的实现

CREATE DEFINER=`root`@`localhost` TRIGGER `healthcare`.`TESTE_BEFORE_INSERT` BEFORE INSERT ON `teste` FOR EACH ROW
BEGIN
    DECLARE last_id integer;
    SET last_id = (SELECT MAX(ID) AS lastID FROM `healthcare`.`teste`);
    IF last_id IS NULL THEN
        SET last_id = 0;
    END IF;
    SET NEW.USER_KEY = conv((1048575-last_id), 10, 32); /* 1048575 = VVVV */
END

欢迎随机化解决方案。

最佳答案

这是您有时可能会感兴趣的例程。这个概念是你有一个用于各种序列的控制表(在系统范围内使用)。简而言之,以下是一个Base36稻草人。它使用 MySQL“意向锁”。

您要求的是Base36数字功能,这提供了这部分功能。如果您这样做,如何将其融入您的设置取决于您。

控制表:

-- drop table if exists sequences;
create table sequences
(   -- the numbers in here are the next numbers free to use
    -- so it is yours once you acquire the INTENTION lock
    -- but do an UPDATE for the next guy by incrementing the number
    -- and COMMIT
    id int auto_increment primary key,
    sectionType varchar(200) not null,
    nextSequence int not null,
    unique key(sectionType) -- perhaps overkill on index but meh
);

-- truncate table sequences;
insert sequences (sectionType,nextSequence) values
('Chassis Serial Number',1),('Engine Block Serial Number',1),('base36number',0);

存储过程:

DROP PROCEDURE IF EXISTS getBase36;
DELIMITER $$
CREATE PROCEDURE getBase36
(   OUT sOutVar CHAR(4) -- out parameter for base36 number
)
BEGIN
    DECLARE num_to_use INT;
    DECLARE i1,i2 INT;
    DECLARE sSendBack CHAR(4);

    -- 0-9 is ascii 48 to 57
    -- A-Z is ascii 65 to 90
    -- 0000 to ZZZZ in that order. 0 to 9 then A etc 36 positions
    -- first char is 0. 36th char is Z. Base36,  0 is 0000 , 35 is 000Z, 36 is 00010
    -- 1.68M possibilities
    -- output ranges from 0000 to ZZZZ
    START TRANSACTION;
    SELECT nextSequence into num_to_use from sequences where sectionType='base36number' FOR UPDATE;
    UPDATE sequences set nextSequence=nextSequence+1 where sectionType='base36number';
    COMMIT; -- because of this, it cannot be a FUNCTION but must be a stored proc, else error 1422
            -- which is Error 1422: Explicit or implicit commit is not allowed in a stored function or trigger

    SET sOutVar='';

    -- IF num_to_use>1679616 THEN
    --  SET sSendBack='----'; 
    --     -- SET sOutVar='----'; -- ran out of space. Think up something else. This was your idea, afterall :p
    --  -- we will drop out of routine
    -- END IF;

    IF num_to_use<1679616 THEN
        -- I don't feel like doing a LOOP for the below
        -- Honestly just because I am tired at the moment.

        SET i2=num_to_use;
        SET i1=FLOOR(i2/46656); -- 46656 is 36 cubed
        IF i1 between 0 and 9 THEN
            SET sSendBack=CHAR(48+i1);
        ELSE
            SET sSendBack=CHAR(65+i1-10);
        END IF;

        SET i2=i2-(i1*46656); 
        SET i1=FLOOR(i2/1296); -- 1296 is 36 squared
        IF i1 between 0 and 9 THEN
            SET sSendBack=CONCAT(sSendBack,CHAR(48+i1));
        ELSE
            SET sSendBack=CONCAT(sSendBack,CHAR(65+i1-10));
        END IF;

        SET i2=i2-(i1*1296); 
        SET i1=FLOOR(i2/36); -- 36 is 36 to the first power
        IF i1 between 0 and 9 THEN
            SET sSendBack=CONCAT(sSendBack,CHAR(48+i1));
        ELSE
            SET sSendBack=CONCAT(sSendBack,CHAR(65+i1-10));
        END IF;

        SET i2=i2-(i1*36);
        SET i1=FLOOR(i2/1); -- 1 is 36 to the 0th
        IF i1 between 0 and 9 THEN
            SET sSendBack=CONCAT(sSendBack,CHAR(48+i1));
        ELSE
            SET sSendBack=CONCAT(sSendBack,CHAR(65+i1-10));
        END IF;
        SET sOutVar=sSendBack; -- base36 number (a string) to OUT parameter
        SELECT num_to_use,sOutVar as yourNumber; -- send out as a resultset too
    ELSE
        SET sSendBack='----';
        SET sOutVar=sSendBack;  -- base36 number (a string) to OUT parameter
        select num_to_use,sOutVar as yourNumber; -- send out as a resultset too
    END IF;
END;$$
DELIMITER ;  

测试:

set @f='';
CALL getBase36(@f); -- initial time for 0000
CALL getBase36(@f); -- 1 is 0001
CALL getBase36(@f); -- 2 is 0002

-- now start testing Boundary conditions

-- pretend we have an INTENTION lock on table and just do an update to test quicker
UPDATE sequences set nextSequence=34 where sectionType='base36number';
CALL getBase36(@f); -- 34 is 000Y
CALL getBase36(@f); -- 35 is 000Z
CALL getBase36(@f); -- 36 is 0010

UPDATE sequences set nextSequence=12345 where sectionType='base36number';
CALL getBase36(@f); -- 12345 is 09IX =9*36*36+18*36+33 
select 9*36*36+18*36+33;
-- = 12345
UPDATE sequences set nextSequence=1679614 where sectionType='base36number';
CALL getBase36(@f); -- 1679614 is ZZZY 
CALL getBase36(@f); -- 1679615 is ZZZZ
CALL getBase36(@f); -- 1679616 is ----
CALL getBase36(@f); -- 1679617 is ----
-- so after 1679615 you can't use your numbering scheme anymore (or my scheme)
-- but in either case, this is the range of numbers you chose to implement
-- up to 1.68M (roughly)

关于mysql - 如何在 MySQL 上使用唯一的字符序列生成触发器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38729098/

相关文章:

mysql - MySQL TRIGGER 可以单独检查所有 INSERT 值吗?

triggers - 在 Oracle 11g 中创建触发器时出现问题

mysql - 如何创建触发器以将更改事件添加到审计日志表中

mysql - Mysql数据库中的空间

php - 从 CodeIgniter 中每个页面所需的模型中检索数据的最佳方法是什么?

python - 安装 mysql-python 时遇到问题

javascript - Google Apps 脚本 onEdit() 不访问外部 API?

php - 使用多个 where 子句进行删除非常慢

php - 从另一个表创建表 - MYSQL 到 MSSQL 转换

wpf - XAML 触发器模板基于另一个元素设置可见性