2016-08-02 43 views
0

o/如何使用唯一的字符序列在MySQL上生成TRIGGER?

我想生成一個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 

最後執行隨機化的解決方案將受到歡迎。

+1

如果你需要它是獨一無二的,我不會讓它是隨機的,尤其是與只有4個字符。我只需要將一個正常的序列轉換爲Base 36,例如http://forums.mysql.com/read.php?98,77546,98265#msg-98265(您可以在Base 62的小寫字母中添加) 。我不確定您是否可以從MySQL中的觸發器調用函數。 – topshot

+0

你需要大寫*還是小寫字母?只有數字和大寫字母的代碼才能正常工作? – Bohemian

+0

我需要爲一千或一百萬用戶生成一些「散列」,並提供一些可擴展性(如果可能的話......只是不需要做一個未來的維護)..但是會被老年用戶使用,所以我想讓儘可能最小的東西。 –

回答

2

以下是您可能在某個時間感興趣的例程。這個概念是你有一個控制表的各種序列(全系統使用)。總之,以下是一名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)