在MySQL中使用自动增量定义复合键

hof1towb  于 2023-06-21  发布在  Mysql
关注(0)|答案(7)|浏览(96)

场景:

我有一个引用两个外键的表,对于这些外键的每个唯一组合,都有自己的auto_increment列。我需要实现一个复合键,这将有助于确定为唯一的行使用这三个(一个外键和一个auto_increment列,和一个非唯一值的其他列)的组合

表:

CREATE  TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT ,
  `app_id` INT NOT NULL ,
  `test_id` INT NOT NULL ,
  `issue_name` VARCHAR(255) NOT NULL ,
primary key (app_id, test_id,sr_no)
);

当然,我的查询肯定有什么问题,因此抛出的错误是:
错误1075:表定义不正确;只能有一个auto列,并且必须将其定义为键

我想达到的目标:

我有一个应用程序表(以app_id作为主键),每个应用程序都有一组待解决的问题,每个应用程序都有多个测试(因此test_id col)sr_no col应该为唯一的app_id和test_id递增。
即,表中的数据应如下所示:

数据库引擎是InnoDB。我希望尽可能简单地实现这一点(即如有可能,避免触发/程序-这是针对其他问题的类似情况提出的建议)。

myzjeezk

myzjeezk1#

你不能让MySQL自动为InnoDB表做这件事--你需要使用触发器或过程,或者使用另一个DB引擎,如MyISAM。只能对单个主键执行自动递增。
类似下面的东西应该可以工作

DELIMITER $$

CREATE TRIGGER xxx BEFORE INSERT ON issue_log
FOR EACH ROW BEGIN
    SET NEW.sr_no = (
       SELECT IFNULL(MAX(sr_no), 0) + 1
       FROM issue_log
       WHERE app_id  = NEW.app_id
         AND test_id = NEW.test_id
    );
END $$

DELIMITER ;
cunj1qz1

cunj1qz12#

您可以使用myISAM和BDB引擎来实现这一点。InnoDB不支持这个。MySQL 5.0参考手册
对于MyISAM和BDB表,您可以在多列索引中的辅助列上指定AUTO_INCREMENT。在这种情况下,AUTO_INCREMENT列的生成值计算为MAX(auto_increment_column)+ 1 WHERE prefix=given-prefix。
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

nkkqxpd9

nkkqxpd93#

我不完全理解你对test_id列的增量要求,但是如果你想要一个~autoincrement序列,在每个唯一的(app_idtest_id)组合上重新启动,你可以执行INSERT...从同一个表中选择,像这样:

mysql> INSERT INTO `issue_log` (`sr_no`, `app_id`, `test_id`, `issue_name`) SELECT
           IFNULL(MAX(`sr_no`), 0) + 1 /* next sequence number */,
           3 /* desired app_id */,
           1 /* desired test_id */,
           'Name of new row'
           FROM `issue_log` /* specify the table name as well */
       WHERE `app_id` = 3 AND `test_id` = 1 /* same values as in inserted columns */

这假定表定义没有声明AUTO_INCREMENT列。您实际上是在使用IFNULL(MAX())+ 1子句模拟自动增量行为,但是手动模拟可以在任意列上工作,这与内置的自动增量不同。
请注意,INSERT... SELECT是一个单一的查询,确保了操作的原子性。InnoDB将间隙锁定适当的索引,许多并发进程可以执行这种查询,同时仍然产生不冲突的序列。

guz6ccqo

guz6ccqo4#

您可以使用unique组合键来表示sr_noapp_idtest_id。您不能在sr_no中使用增量,因为它不是唯一的。

CREATE TABLE IF NOT EXISTS `issue_log` (
  `sr_no` int(11) NOT NULL,
  `app_id` int(11) NOT NULL,
  `test_id` int(11) NOT NULL,
  `issue_name` varchar(255) NOT NULL,
  UNIQUE KEY `app_id` (`app_id`,`test_id`,`sr_no`)
) ENGINE=InnoDB ;

我已经注解掉了sql fiddle中的unique constraint violation以进行演示(删除schema第22行中的#并重新构建schema)

gj3fmq9x

gj3fmq9x5#

这就是我想要的

id    tenant
    1        1
    2        1
    3        1
    1        2
    2        2
    3        2
    1        3
    2        3
    3        3

我当前的表定义是

CREATE TABLE `test_trigger` (
  `id` BIGINT NOT NULL,
  `tenant` varchar(255) NOT NULL,
  PRIMARY KEY (`id`,`tenant`)
);

我创建了一个表来存储每个租户的当前ID。

CREATE TABLE `get_val` (
  `tenant` varchar(255) NOT NULL,
  `next_val` int NOT NULL,
  PRIMARY KEY (`tenant`,`next_val`)
) ENGINE=InnoDB ;

然后我创造了这个触发器来解决我的问题

DELIMITER $$
CREATE TRIGGER trigger_name 
BEFORE INSERT 
ON test_trigger 
FOR EACH ROW
    BEGIN
        UPDATE get_val SET next_val = next_val + 1 WHERE tenant = new.tenant;
        set new.id = (select next_val from get_val where tenant=new.tenant);
      END$$
    DELIMITER ;

这种方法也将是线程安全的,因为由于触发器中的更新查询,针对相同租户的任何插入将顺序地发生,并且针对不同租户的插入将并行发生。

pgvzfuti

pgvzfuti6#

只需在自动递增列上添加键(sr_no):

CREATE  TABLE `issue_log` (
 `sr_no` INT NOT NULL AUTO_INCREMENT ,
 `app_id` INT NOT NULL ,
 `test_id` INT NOT NULL ,
 `issue_name` VARCHAR(255) NOT NULL ,
  primary key (app_id, test_id,sr_no),
  key (`sr_no`)
);
7vux5j2d

7vux5j2d7#

为什么不尝试改变声明字段作为主键的位置,因为当你使用“auto_increment”时,它必须作为第一个引用。就像下面的例子

CREATE  TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT ,
  `app_id` INT NOT NULL ,
  `test_id` INT NOT NULL ,
  `issue_name` VARCHAR(255) NOT NULL ,
primary key (sr_no,app_id, test_id)
);

相关问题