2013-03-25 66 views
0

我有一个独特的PK'id',它被设置为auto_increment。我有第二个叫做'label'的字段,它是一个字母数字字段(例如W1000),用于使用PHP逻辑在每个插入上递增。多个auto_increment字段

“标签”字段可能包含许多字母前缀之一,后跟增加的数字。每个前缀应该独立增加。例如,表格可能有W1000和F1123。下一个W将是W1001,下一个F将是F1124。

当前方法(PHP选择最大标签,插入最大标签+1)会创建一个竞争条件,偶尔会得到一个重复的“标签”。我需要解决这些重复的“标签”,并确保此字段是唯一的。如果有帮助,我愿意将前缀和数字分成两个字段。

完成此操作的最佳方法是什么?

+0

将字段设置为'UNIQUE'? – hjpotter92 2013-03-25 02:00:54

回答

1

避免生成重复标签值的一种方法是使用MyISAM表来生成唯一的序列号。 MyISAM支持您需要的AUTO_INCREMENT行为。

参见“MyISAM的注意事项”一节中的MySQL参考3.6.9. Using AUTO_INCREMENT

对于这种方法,你可以创建一个单独的MyISAM表;该表的目的是生成唯一的序列号:例如,

CREATE TABLE foo 
(prefix VARHCAR(1) NOT NULL 
, num  INT UNSIGNED AUTO_INCREMENT 
, PRIMARY KEY (prefix, num) 
) Engine=MyISAM 

假设标签前缀是一个字符,其余是数字:

INSERT INTO foo (prefix, num) 
SELECT SUBSTR(t.label,1,1) AS prefix 
    , MAX(SUBSTR(t.label,2,8) AS num 
    FROM mytable 
GROUP BY SUBSTR(t.label,1,1) 

去获得新的序列号,插入一行到新表,为前缀的值,并为NUM列NULL,并检索插入的Num列的值:

INSERT INTO foo (prefix,num) VALUES ('W',NULL); 
SELECT LAST_INSERT_ID(); 

你可以用它来构建的价值在你用于label列r原始表格。

请注意,它只是具有所需行为的MyISAM引擎(为每个前缀分别增加AUTO_INCREMENT序列)。您的原始表可以是任何引擎。

这种方法避免了竞争条件,但是由于在MyISAM表上插入了独占锁,所以引入了并发瓶颈。


的另一种方式,以避免竞态条件是获得对表的排他锁,然后做一个SELECT MAX(),然后执行插入,然后松开该锁。但是这种方法引入了更多的并发瓶颈,即对单个资源的访问序列化。


如果你的问题是关于识别重复的现有label值,那么这个查询获取你有“重复”标签的行。 (这是挑选出只有一行,每个重复的标签。)

SELECT t.label 
    , MAX(t.id) 
    FROM mytable t 
GROUP BY t.label 
HAVING COUNT(1) > 1 

要更新标签是唯一的,你需要生成这些行的新标签。

在单个SQL语句中完成该操作有点棘手。我试图提出一个声明,但它被破坏了,我没有时间去修复它。