web-dev-qa-db-ja.com

MySQLでAuto_incrementを使用して複合キーを定義する

シナリオ:

2つの外部キーを参照するテーブルがあり、これらの外部キーの一意の組み合わせごとに、独自のauto_increment列があります。これら3つの組み合わせ(1つの外部キーと1つのauto_increment列、および1つの列が一意でない値を持つ)を組み合わせて使用​​して、行を一意として識別するのに役立つ複合キーを実装する必要があります。

表:

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:テーブルの定義が正しくありません。自動列は1つしか存在できず、キーとして定義する必要があります

私が達成しようとしていること:

アプリケーションテーブル(app_idを主キーとして)があり、各アプリケーションには解決すべき問題のセットがあり、各アプリケーションには複数のテスト(test_id col)があります。sr_nocolは、一意のapp_idおよびtest_idに対して増加する必要があります。

つまり、表のデータは次のようになります。

enter image description here

データベースエンジンはInnoDBです。私はこれをできるだけ簡単に実現したいと考えています(つまり、可能であればトリガー/手順を避けます-これは他の質問の類似のケースで提案されました)。

23
Nirav Zaveri

MySQLでInnoDBテーブルに対してこれを自動的に行うことはできません。トリガーまたはプロシージャを使用するか、MyISAMなどの別のDBエンジンを使用する必要があります。自動インクリメントは、単一の主キーに対してのみ実行できます。

次のようなものがうまくいくはずです

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

これは、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

7
Goran

test_id列の増分要件を完全に理解していませんが、(app_idtest_id)の一意の組み合わせごとに再起動する〜autoincrementシーケンスが必要な場合は、次のように、INSERT ... SELECT FROM同じテーブル:

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は適切なインデックスをギャップロックし、競合しないシーケンスを生成しながら、多くの同時プロセスがこの種のクエリを実行できます。

3
Rolf NB

sr_noapp_idtest_idには nique 複合キーを使用できます。これは一意ではないため、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 の一意制約違反をコメントアウトして示しています(スキーマの22行目の#を削除してスキーマを再構築します)

3
david strachan