web-dev-qa-db-fra.com

Définition d'une clé composite avec incrémentation automatique dans MySQL

Scénario:

J'ai une table qui référence deux clés étrangères, et pour chaque combinaison unique de ces clés étrangères, a sa propre colonne auto_increment. J'ai besoin d'implémenter une clé composite qui aidera à identifier la ligne comme unique en utilisant une combinaison de ces trois (une clé étrangère et une colonne auto_increment, et une autre colonne avec des valeurs non uniques)

Tableau:

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)
);

Bien sûr, il doit y avoir un problème avec ma requête, à cause de laquelle l'erreur renvoyée est:

ERREUR 1075: définition de table incorrecte; il ne peut y avoir qu'une seule colonne automatique et elle doit être définie comme une clé

Ce que j'essaie de réaliser:

J'ai une table d'application (avec app_id comme clé primaire), chaque application a un ensemble de problèmes à résoudre, et chaque application a plusieurs nombres de tests (donc le col test_id) Le col sr_no doit incrémenter pour app_id et test_id uniques.

c'est-à-dire que les données du tableau doivent ressembler à:

enter image description here

Le moteur de base de données est InnoDB. Je veux y parvenir avec autant de simplicité que possible (c'est-à-dire éviter les déclencheurs/procédures si possible - ce qui a été suggéré pour des cas similaires sur d'autres questions).

23
Nirav Zaveri

MySQL ne peut pas faire cela automatiquement pour vous pour les tables InnoDB - vous devez utiliser un déclencheur ou une procédure, ou utiliser un autre moteur de base de données tel que MyISAM. L'incrémentation automatique ne peut être effectuée que pour une seule clé primaire.

Quelque chose comme ce qui suit devrait fonctionner

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 ;
29
noz

Vous pouvez le faire avec les moteurs myISAM et BDB. InnoDB ne prend pas cela en charge. Citation du manuel de référence MySQL 5.0.

Pour les tables MyISAM et BDB, vous pouvez spécifier AUTO_INCREMENT sur une colonne secondaire dans un index à plusieurs colonnes. Dans ce cas, la valeur générée pour la colonne AUTO_INCREMENT est calculée comme MAX (auto_increment_column) + 1 WHERE prefix = given-prefix.

http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

7
Goran

Je ne comprends pas parfaitement votre exigence d'incrémentation sur le test_id, mais si vous voulez une séquence ~ d'auto-incrémentation qui redémarre sur chaque combinaison unique de (app_id, test_id), vous pouvez faire un INSERT ... SELECT FROM la même table, comme ceci:

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 */

Cela suppose une définition de table sans colonne AUTO_INCREMENT déclarée. Vous émulez essentiellement le comportement d'auto-incrémentation avec la clause IFNULL (MAX ()) + 1, mais l'émulation manuelle fonctionne sur des colonnes arbitraires, contrairement à l'auto-incrémentation intégrée.

Notez que INSERT ... SELECT étant une requête unique garantit l'atomicité de l'opération. InnoDB verrouillera les index appropriés et de nombreux processus simultanés peuvent exécuter ce type de requête tout en produisant des séquences non conflictuelles.

3
Rolf NB

Vous pouvez utiliser une clé composite nique pour sr_no, app_id & test_id. Vous ne pouvez pas utiliser incrémentiel dans sr_no car ce n'est pas unique.

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 ;

J'ai commenté une violation de contrainte unique dans sql fiddle pour démontrer (supprimer # à la ligne 22 du schéma et reconstruire le schéma)

3
david strachan