MySQL:auto_incrementが主キーだけに制限されているのはなぜですか?


10

MySQLがauto_incrementカラムを主キーに制限していることを知っています。どうしてこれなの?私の最初の考えは、この値を取得するためにロックする必要があるカウンターテーブルがどこかにある可能性があるため、これはパフォーマンスの制限だということです。

同じテーブルに複数のauto_increment列を含めることができないのはなぜですか?

ありがとう。


トランザクションで@popを使用するのを忘れたことに気づきました。私は例を再試行して、それを私の答えの下部に投稿しました!!!
RolandoMySQLDBA 2011年

私はこの質問が好きです。金曜日の夜にはあまりしませんが、MySQLをすぐに使えるようになったからです。+1 !!!
RolandoMySQLDBA

回答:


9

主キーではないauto_increment列が必要なのはなぜですか?

列をauto_incrementにしたい場合は、定義上、その列に意味のあるデータを格納していません。意味のない情報を保存するのが理にかなっている唯一のケースは、合成主キーが必要な特殊なケースです。その場合、あるエンティティの一部の属性が変更されたために誰かが将来一緒に来てデータを変更したいというリスクがないため、情報がないことは利点です。

同じテーブルに複数のauto_increment列があると、さらに奇妙に見えます。2つの列は同じデータを持ちます。つまり、同じアルゴリズムによって生成され、結局同時に入力されます。十分な数の同時セッションがあった場合、それらがわずかに同期しなくなる可能性のある実装を考え出すことができると思います。しかし、それがアプリケーションでどのように役立つかは想像できません。


もっと理論的な質問でした-私は複数のauto_incrementカラムを使用する実用的な使い方はありません。なぜそれが不可能であるのかについて人々の説明を聞きたかったのです。お時間を割いていただきありがとうございます!:)
クリストファーアームストロング

2
「主キーではないauto_increment列が必要なのはなぜですか?」-個人的にはいくつかの理由が考えられます。しかし、それはOTです。:-)
Denis de Bernardy

私は今このようなことをしています-そしてそれは無意味なデータではありません。これまでにテーブルに挿入されたすべてのレコードの数を知る必要がありますが、すでにより有用な主キーがあります。これは、すべての新しいレコードにその価値があるため、それを解決します。(弱い)アナロジーは、「あなたは私たちの10,000番目の顧客」という要件です。顧客は時間の経過とともに削除されるため、COUNT(*)は無効です。
Cylindric

主キーではないauto_increment列が必要なのはなぜですか?
phil_w 2014年

2
主キーではないauto_increment列が必要なのはなぜですか?考えられる理由は次のとおりです。PKは行を物理的に順序付けるためにも使用されるため。例:ユーザー向けのメッセージを保存するとします。ユーザーごとにすべてのメッセージを読み取る必要があるため、効率的に取得するためにそれらを一緒にしたいとします。innodbはそれらをPKでソートするので、次のようにすることができます。テーブルメッセージを作成します(ユーザー、ID、テキスト、主キー(ユーザー、ID))
phil_w

8

実際、AUTO_INCREMENT属性はPRIMARY KEY(これ以上)に限定されません。以前のバージョンではそうだった-間違いなく3.23で、たぶん4.0だった。それでも4.1以降のすべてのバージョンのMySQLマニュアルは次のようになっています

テーブルごとにAUTO_INCREMENTカラムは1つだけ存在でき、インデックスを付ける必要があり、DEFAULT値を持つことはできません。

そのため、主キーではないテーブルにAUTO_INCREMENT列を含めることができます。それが理にかなっている場合でも、別のトピックです。

また、AUTO_INCREMENT列は常に整数型(技術的には浮動小数点型も許可されています)である必要があり、UNSIGNEDである必要があります。SIGNEDタイプは、キースペースの半分を浪費するだけでなく、誤って負の値が挿入された場合にも大きな問題を引き起こす可能性があります。

最後に、MySQL 4.1以降では、BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUEの型エイリアスSERIALが定義されています。


1
+1は、auto_incrementがPKに限定されないことを示します。ただし、代理キーに負の数を使用すると、「大きな問題」が発生すると考える理由がわかりません。
nvogel

4

異なるデータベースにはauto_incrementを提供するための独自のアプローチがあるため、これは興味深い質問です。

MySQL:テーブル内の行を一意に識別するために生成されるauto_incrementキーは1つだけです。その理由の多くは説明されておらず、実装のみです。データ型に応じて、auto_increment値はデータ型の長さ(バイト単位)によって固定されます。

  • 最大TINYINTは127です
  • 最大UNSIGNED TINTINTは255です
  • 最大INTは2147483647です
  • 最大UNSIGNED INTは4294967295

PostgreSQL

内部データ型シリアルは、1から2,147,483,647への自動インクリメントに使用されます。bigserialを使用すると、より広い範囲が許可されます。

Oracle:SEQUENCEと呼ばれるスキーマオブジェクトは、nextval関数を呼び出すだけで新しい数値を作成できます。PostgreSQLにもこのようなメカニズムがあります。

他のDBがそれらをどのように指定するかを提供する素晴らしいURLは次のとおりです。http//www.w3schools.com/sql/sql_autoincrement.asp

さてあなたの質問に関して、もしあなたが本当に単一のテーブルに複数のauto_incrementカラムを持ちたいなら、あなたはそれをエミュレートする必要があります。

これをエミュレートする必要がある2つの理由:

  1. MySQLは、PostgreSQL、Oracle、SQL Server、およびMS Accessと同様に、テーブルごとに1つの増分列のみを収容します。
  2. MySQLには、OracleやPostgreSQLのようなSEQUENCEスキーマオブジェクトはありません。

どのようにエミュレートしますか?

auto_increment列が1つだけの複数のテーブルを使用し、それらをターゲットテーブルの目的の列にマッピングします。次に例を示します。

この例をコピーして貼り付けます。

use test
DROP TABLE IF EXISTS teacher_popquizzes;
CREATE TABLE teacher_popquizzes
(
    teacher varchar(20) not null,
    class varchar(20) not null,
    pop_mon INT NOT NULL DEFAULT 0,
    pop_tue INT NOT NULL DEFAULT 0,
    pop_wed INT NOT NULL DEFAULT 0,
    pop_thu INT NOT NULL DEFAULT 0,
    pop_fri INT NOT NULL DEFAULT 0,
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
INSERT INTO teacher_popquizzes (teacher,class) VALUES
('mr jackson','literature'),
('mrs andrews','history'),
('miss carroll','spelling');
DROP TABLE IF EXISTS mon_seq;
DROP TABLE IF EXISTS tue_seq;
DROP TABLE IF EXISTS wed_seq;
DROP TABLE IF EXISTS thu_seq;
DROP TABLE IF EXISTS fri_seq;
CREATE TABLE mon_seq
(
    val INT NOT NULL DEFAULT 0,
    nextval INT NOT NULL DEFAULT 1,
    PRIMARY KEY (val)
);
CREATE TABLE tue_seq LIKE mon_seq;
CREATE TABLE wed_seq LIKE mon_seq;
CREATE TABLE thu_seq LIKE mon_seq;
CREATE TABLE fri_seq LIKE mon_seq;
BEGIN;
INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM mon_seq;
UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 2;
COMMIT;
BEGIN;
INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM tue_seq;
UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 1;
COMMIT;
BEGIN;
INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
SELECT nextval INTO @pop FROM wed_seq;
UPDATE teacher_popquizzes SET pop_wed = pop_wed + 1 WHERE id = 2;
COMMIT;
SELECT * FROM teacher_popquizzes;

これにより、教師向けのポップクイズの表が作成されます。また、学校の週の各日に1つずつ、5つのシーケンスエミュレーターを作成しました。各シーケンスエミュレータは、val列に値0を挿入することで機能します。シーケンスエミュレーターが空の場合、val 0、nextval 1から始まります。空でない場合、nextval列が増分されます。次に、シーケンスエミュレータからnextval列をフェッチできます。

この例のサンプル結果は次のとおりです。

mysql> CREATE TABLE teacher_popquizzes
    -> (
    ->     teacher varchar(20) not null,
    ->     class varchar(20) not null,
    ->     pop_mon INT NOT NULL DEFAULT 0,
    ->     pop_tue INT NOT NULL DEFAULT 0,
    ->     pop_wed INT NOT NULL DEFAULT 0,
    ->     pop_thu INT NOT NULL DEFAULT 0,
    ->     pop_fri INT NOT NULL DEFAULT 0,
    ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
    -> );
Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO teacher_popquizzes (teacher,class) VALUES
    -> ('mr jackson','literature'),
    -> ('mrs andrews','history'),
    -> ('miss carroll','spelling');
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> DROP TABLE IF EXISTS mon_seq;
Query OK, 0 rows affected (0.06 sec)

mysql> DROP TABLE IF EXISTS tue_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS wed_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS thu_seq;
Query OK, 0 rows affected (0.05 sec)

mysql> DROP TABLE IF EXISTS fri_seq;
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE mon_seq
    -> (
    ->     val INT NOT NULL DEFAULT 0,
    ->     nextval INT NOT NULL DEFAULT 1,
    ->     PRIMARY KEY (val)
    -> );
Query OK, 0 rows affected (0.12 sec)

mysql> CREATE TABLE tue_seq LIKE mon_seq;
Query OK, 0 rows affected (0.09 sec)

mysql> CREATE TABLE wed_seq LIKE mon_seq;
Query OK, 0 rows affected (0.08 sec)

mysql> CREATE TABLE thu_seq LIKE mon_seq;
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE fri_seq LIKE mon_seq;
Query OK, 0 rows affected (0.14 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM mon_seq;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 2 rows affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = pop_tue + 1 WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM wed_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_wed = pop_wed + 1 WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT * FROM teacher_popquizzes;
+--------------+------------+---------+---------+---------+---------+---------+----+
| teacher      | class      | pop_mon | pop_tue | pop_wed | pop_thu | pop_fri | id |
+--------------+------------+---------+---------+---------+---------+---------+----+
| mr jackson   | literature |       0 |       1 |       0 |       0 |       0 |  1 |
| mrs andrews  | history    |       0 |       1 |       1 |       0 |       0 |  2 |
| miss carroll | spelling   |       0 |       0 |       0 |       0 |       0 |  3 |
+--------------+------------+---------+---------+---------+---------+---------+----+
3 rows in set (0.00 sec)

mysql>

MySQLで複数の自動インクリメント値が本当に必要な場合、これがエミュレートする最も近い方法です。

試してみる !!!

更新2011-06-23 21:05

私の例では、@ pop値を使用していないことに気づきました。

今回は「pop_tue = pop_tue + 1」を「pop_tue = @pop」に置き換えて、例を再試行しました。

mysql> use test
Database changed
mysql> DROP TABLE IF EXISTS teacher_popquizzes;
Query OK, 0 rows affected (0.05 sec)

mysql> CREATE TABLE teacher_popquizzes
    -> (
    ->     teacher varchar(20) not null,
    ->     class varchar(20) not null,
    ->     pop_mon INT NOT NULL DEFAULT 0,
    ->     pop_tue INT NOT NULL DEFAULT 0,
    ->     pop_wed INT NOT NULL DEFAULT 0,
    ->     pop_thu INT NOT NULL DEFAULT 0,
    ->     pop_fri INT NOT NULL DEFAULT 0,
    ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> INSERT INTO teacher_popquizzes (teacher,class) VALUES
    -> ('mr jackson','literature'),
    -> ('mrs andrews','history'),
    -> ('miss carroll','spelling');
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> DROP TABLE IF EXISTS mon_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS tue_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS wed_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> DROP TABLE IF EXISTS thu_seq;
Query OK, 0 rows affected (0.01 sec)

mysql> DROP TABLE IF EXISTS fri_seq;
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TABLE mon_seq
    -> (
    ->     val INT NOT NULL DEFAULT 0,
    ->     nextval INT NOT NULL DEFAULT 1,
    ->     PRIMARY KEY (val)
    -> );
Query OK, 0 rows affected (0.08 sec)

mysql> CREATE TABLE tue_seq LIKE mon_seq;
Query OK, 0 rows affected (0.09 sec)

mysql> CREATE TABLE wed_seq LIKE mon_seq;
Query OK, 0 rows affected (0.13 sec)

mysql> CREATE TABLE thu_seq LIKE mon_seq;
Query OK, 0 rows affected (0.11 sec)

mysql> CREATE TABLE fri_seq LIKE mon_seq;
Query OK, 0 rows affected (0.08 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 1 row affected (0.01 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = @pop WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO tue_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 2 rows affected (0.00 sec)

mysql> SELECT nextval INTO @pop FROM tue_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_tue = @pop WHERE id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO wed_seq (val) VALUES (0) ON DUPLICATE KEY UPDATE nextval = nextval + 1;

Query OK, 1 row affected (0.01 sec)

mysql> SELECT nextval INTO @pop FROM wed_seq;
Query OK, 1 row affected (0.00 sec)

mysql> UPDATE teacher_popquizzes SET pop_wed = @pop WHERE id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM teacher_popquizzes;
+--------------+------------+---------+---------+---------+---------+---------+----+
| teacher      | class      | pop_mon | pop_tue | pop_wed | pop_thu | pop_fri | id |
+--------------+------------+---------+---------+---------+---------+---------+----+
| mr jackson   | literature |       0 |       2 |       0 |       0 |       0 |  1 |
| mrs andrews  | history    |       0 |       1 |       1 |       0 |       0 |  2 |
| miss carroll | spelling   |       0 |       0 |       0 |       0 |       0 |  3 |
+--------------+------------+---------+---------+---------+---------+---------+----+
3 rows in set (0.00 sec)

mysql>

要約は完全に正しくありません。PostgreSQLは、Oracleと同様に、任意の数の自動インクリメント(シリアル)列をサポートします(どちらの場合も、列にはシーケンスの値が入力されます)。PostgreSQLは、bigserial2,147,483,647よりはるかに広い範囲を提供するデータ型も提供します
a_horse_with_no_name

@a_horse_with_no_name:見落として申し訳ありません。私はまだpostgresqlの旅人です。後で回答を更新します。私はiPhoneからの道の答えです。良い一日を過ごしてください!
RolandoMySQLDBA

0

XLが言うように、それは主キーだけに限定されません。テーブルごとにこのような列を1つしか持てないことは潜在的な制限ですが、最善の回避策は、別のテーブルで必要な数の数値を生成し、必要な場所に挿入することです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.