How to implement an auto_increment composite primary key with MySQL InnoDB?

扶醉桌前 提交于 2019-12-24 00:29:32

问题


I have a table which has a composite primary key made up of one non-auto_increment column and one auto_increment column. The auto_increment column needs to increment individually for each of the non-auto_increment column values (more on this later). The storage engine is InnoDB. I don't wish to lock the table because of performance concerns. After inserting a value, a means to retrieve the last auto_increment value must be available.

The below script works at first, but the last INSERT results in id, checkingaccounts_id is 3, 2, but 1, 2 is desired. This is what I meant by The auto_increment column needs to increment individually for each of the non-auto_increment column values

Trigger and Stored Procedures are both acceptable, and so is a PHP/PDO application solution which somehow mimics the MySQL auto_increment behavior.

mysql> EXPLAIN checkingaccounts;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| data  | varchar(45) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> EXPLAIN checks;
+---------------------+-------------+------+-----+---------+----------------+
| Field               | Type        | Null | Key | Default | Extra          |
+---------------------+-------------+------+-----+---------+----------------+
| id                  | int(11)     | NO   | PRI | NULL    | auto_increment |
| checkingaccounts_id | int(11)     | NO   | PRI | NULL    |                |
| data                | varchar(45) | YES  |     | NULL    |                |
+---------------------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> INSERT INTO checkingaccounts(id, data) VALUES(0,'bla');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO checkingaccounts(id, data) VALUES(0,'bla');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM checkingaccounts;
+----+------+
| id | data |
+----+------+
|  1 | bla  |
|  2 | bla  |
+----+------+
2 rows in set (0.00 sec)

mysql> INSERT INTO checks(id,checkingaccounts_id,data) VALUES(0,1,'bla');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO checks(id,checkingaccounts_id,data) VALUES(0,1,'bla');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM checks;
+----+---------------------+------+
| id | checkingaccounts_id | data |
+----+---------------------+------+
|  1 |                   1 | bla  |
|  2 |                   1 | bla  |
+----+---------------------+------+
2 rows in set (0.00 sec)

mysql> INSERT INTO checks(id,checkingaccounts_id,data) VALUES(0,2,'bla');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM checks;
+----+---------------------+------+
| id | checkingaccounts_id | data |
+----+---------------------+------+
|  1 |                   1 | bla  |
|  2 |                   1 | bla  |
|  3 |                   2 | bla  |
+----+---------------------+------+
3 rows in set (0.00 sec)

mysql>

回答1:


Remove the auto_increment feature, try a stored procedure instead:

CREATE PROCEDURE insertChecks
     (IN AccID int(9), IN data varchar(50))
BEGIN
    DECLARE cid INT DEFAULT 1;

    SELECT (COUNT(*) + 1) INTO cid 
    FROM checks 
    WHERE checkingaccounts_id = AccID;

    INSERT INTO checks(id, checkingaccounts_id, data) 
    VALUES(cid, AccID, data);
END

And

call insertChecks(1,'bla');
call insertChecks(1,'bla');
call insertChecks(2,'bla');

Solution 2:

CREATE PROCEDURE insertChecks 
    (IN AccID int(9), IN data varchar(50))
BEGIN
    INSERT INTO checks(id, checkingaccounts_id, data) 
        SELECT (COUNT(*) + 1), AccID, data 
        FROM checks 
        WHERE checkingaccounts_id = AccID;
END



回答2:


Create a MyISAM table to create the auto_increment id only, and use a trigger to use this id for the targeted table. If there are more than one InnoDB tables that need a composite auto_incrementing primary key, add an extra primary key to the MyISAM table.

Disadvantages:

  1. No foreign key constraints allowed on the MyISAM table, however, hopefully a trigger removes the risk.
  2. Requires an additional table.

Advantages

  1. Primary key valves are not reused if deleted.
  2. Client just uses normal SQL queries and not a stored procedure.
  3. Maybe less maintenance than a stored procedure since adding a column to a table doesn't require the trigger to be modified.

-- MySQL Script generated by MySQL Workbench
-- 07/08/16 05:12:11
-- Model: New Model    Version: 1.0
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`a`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`a` (
  `id` INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`t1`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`t1` (
  `a_id` INT NOT NULL,
  `id` INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`, `a_id`),
  CONSTRAINT `fk_t1_a1`
    FOREIGN KEY (`a_id`)
    REFERENCES `mydb`.`a` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`t2`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`t2` (
  `a_id` INT NOT NULL,
  `id` INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`, `a_id`),
  CONSTRAINT `fk_t2_a1`
    FOREIGN KEY (`a_id`)
    REFERENCES `mydb`.`a` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`t3`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`t3` (
  `a_id` INT NOT NULL,
  `id` INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`, `a_id`),
  CONSTRAINT `fk_t3_a1`
    FOREIGN KEY (`a_id`)
    REFERENCES `mydb`.`a` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`inc`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`inc` (
  `a_id` INT NOT NULL,
  `id` INT NOT NULL AUTO_INCREMENT,
  `type` CHAR(4) NOT NULL,
  PRIMARY KEY (`a_id`, `type`, `id`))
ENGINE = MyISAM;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
USE `mydb`;

DELIMITER $$
USE `mydb`$$
CREATE TRIGGER `t1_BINS` BEFORE INSERT ON `t1` FOR EACH ROW
BEGIN 
INSERT INTO inc(a_id,type) VALUES(NEW.a_id,'t1');
SET NEW.id=LAST_INSERT_ID();
END$$

USE `mydb`$$
CREATE TRIGGER `t2_BINS` BEFORE INSERT ON `t2` FOR EACH ROW
BEGIN 
INSERT INTO inc(a_id,type) VALUES(NEW.a_id,'t2');
SET NEW.id=LAST_INSERT_ID();
END$$

USE `mydb`$$
CREATE TRIGGER `t3_BINS` BEFORE INSERT ON `t3` FOR EACH ROW
BEGIN 
INSERT INTO inc(a_id,type) VALUES(NEW.a_id,'t3');
SET NEW.id=LAST_INSERT_ID();
END$$


DELIMITER ;

Testing

mysql> insert into a(id) VALUES(null);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(a_id) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM t1;
+------+----+
| a_id | id |
+------+----+
|    1 |  1 |
+------+----+
1 row in set (0.00 sec)

mysql> insert into t1(a_id) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(a_id) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM t1;
+------+----+
| a_id | id |
+------+----+
|    1 |  1 |
|    1 |  2 |
|    1 |  3 |
+------+----+
3 rows in set (0.00 sec)

mysql> insert into t2(a_id) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM t2;
+------+----+
| a_id | id |
+------+----+
|    1 |  1 |
+------+----+
1 row in set (0.00 sec)

mysql> insert into a(id) VALUES(null);
Query OK, 1 row affected (0.00 sec)

mysql> insert into a(id) VALUES(null);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(a_id) VALUES(2);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(a_id) VALUES(3);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1(a_id) VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM t1;
+------+----+
| a_id | id |
+------+----+
|    1 |  1 |
|    1 |  2 |
|    1 |  3 |
|    1 |  4 |
|    2 |  1 |
|    3 |  1 |
+------+----+
6 rows in set (0.00 sec)

mysql>


来源:https://stackoverflow.com/questions/38227145/how-to-implement-an-auto-increment-composite-primary-key-with-mysql-innodb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!