PostgreSQLの自動インクリメント


579

MySQLからPostgreSQLに切り替えて、自動インクリメント値をどうやって作成するのか疑問に思っていました。PostgreSQLのドキュメントで「シリアル」というデータ型を見ましたが、それを使用すると構文エラーが発生します(v8.0)。


9
取得したクエリとエラーを提供する場合-おそらく誰かがクエリの何が問題かを教えてくれるでしょう。

2
私の最初のヒットも多すぎます。それは、関連性があるのに十分な見解を得られる質問なので、投票してみてください。PSそれを行う方法がわからない場合、それは簡単ではありません。
baash05 2012年

1
クライアントドライバーがNpgsqlの場合、SERIALが推奨されます。プロバイダーは、SELECT currval(pg_get_serial_sequence( 'table'、 'column'))を使用して、INSERT後に内部的に新しい値を選択しています。基になる列がシリアル型(数値型+明示的なシーケンスなど)でない場合、これは失敗します
Olivier MATROT

ちょうど好奇心のために...なぜ誰かがMySQLからPostgreSqlに移行しなければならないのですか?
villamejia

17
...さらに良いです。
Rohmer 2017年

回答:


702

はい、SERIALは同等の機能です。

CREATE TABLE foo (
id SERIAL,
bar varchar);

INSERT INTO foo (bar) values ('blah');
INSERT INTO foo (bar) values ('blah');

SELECT * FROM foo;

1,blah
2,blah

SERIALは、シーケンスに関するテーブル作成時間マクロです。SERIALを既存の列に変更することはできません。


19
テーブル名を引用するのは本当に悪い習慣です
エヴァン・キャロル

71
大文字と小文字が混在するDBを継承したため、テーブル名を引用することは習慣であり、テーブル名を引用することは使用の要件です。
Trey

26
@エヴァン・キャロル-なぜそれが悪い習慣なのですか?
クリスチャン

27
あなたがテーブルを持っていない限り、理由"Table""table"そしてちょうど引用符で囲まれていない、それを残し、それを正規化table。慣例では、Pgで引用符を使用しないでください。あなたは、あなたがしたい場合は、外観のために混在ケース名を使用し、それを必要としないことができます:CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;意志として、動作しますSELECT * FROM foobar
エヴァンキャロル

26
Postgresのドキュメントごとに、いずれかの一貫引用かにunquote:postgresql.org/docs/current/interactive/...
Καrτhικ

225

などの他の整数データ型を使用できますsmallint

例:

CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
    user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;

ユーザーのシリアルデータタイプではなく、独自のデータタイプを使用する方が適切です。


11
列のデフォルトを設定することにより、PostgreSQLで作成したばかりのテーブルを変更できるため、これが実際に良い答えだと思います(CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.htmlを読んだ後) 。ただし、所有者を変更した理由はよくわかりません。
JayC、2011

12
@JayC:ドキュメントから:最後に、シーケンスは列の「所有者」としてマークされているため、列またはテーブルが削除されると、シーケンスが削除されます。
user272735

9
postgresコミュニティがautoincrementキーワードを再発明しないのはなぜですか?
Deo博士、2012年

2
@Dr Deo:彼らはautoincrementキーワードの代わりにシリアルを使用します、理由はわかりません:)
Ahmad

4
より小さなデータ型が必要な場合は、smallserialもあります。
beldaz 2013年

110

すでに存在するテーブルのidにシーケンスを追加する場合は、次のように使用できます。

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');

シーケンスとは何ですか?AUTO_INCREMENTはどこにありますか?
グリーン

23
@Green:AUTO_INCREMENTはSQL標準の一部ではなく、MySQLに固有です。シーケンスは、PostgreSQLで同様の処理を行うものです。
beldaz 2013年

5
'id SERIAL'を使用すると、PostgreSQLにシーケンスが自動的に作成されます。そのシーケンスの名前は、<テーブル名> _ <列名> _seqになります
Jude Niroshan

使う必要はありませんALTER COLUMN user_idか?
アレック

この方法を試しましたが、エラーが発生しました。ERROR: syntax error at or near "DEFAULT"何か提案はありますか?
エリーフィアルコフ2018

44

シーケンスはMySQLのauto_incrementと同等であるように見えますが、微妙ですが重要な違いがあります。

1.失敗したクエリによりシーケンス/シリアルが増加する

クエリが失敗すると、シリアル列が増加します。これにより、行の削除だけでなく、失敗したクエリの断片化が発生します。たとえば、PostgreSQLデータベースで次のクエリを実行します。

CREATE TABLE table1 (
  uid serial NOT NULL PRIMARY KEY,
  col_b integer NOT NULL,
  CHECK (col_b>=0)
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

SELECT * FROM table1;

次の出力が表示されます。

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
(2 rows)

uidが1から2ではなく1から3にどのように変化するかに注意してください。

これは、手動で独自のシーケンスを作成する場合にも発生します。

CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
    col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
    col_b integer NOT NULL,
    CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;

MySQLの違いをテストする場合は、MySQLデータベースで次を実行します。

CREATE TABLE table1 (
  uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  col_b int unsigned NOT NULL
);

INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);

あなたは断片化なしで以下を得るべきです:

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
+-----+-------+
2 rows in set (0.00 sec)

2.シリアル列の値を手動で設定すると、今後のクエリが失敗する可能性があります。

これは、前の回答で@trevによって指摘されました。

これを手動でシミュレートするには、後で「衝突」するuidを4に設定します。

INSERT INTO table1 (uid, col_b) VALUES(5, 5);

テーブルデータ:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
(3 rows)

別の挿入を実行します。

INSERT INTO table1 (col_b) VALUES(6);

テーブルデータ:

 uid | col_b 
-----+-------
   1 |     1
   3 |     2
   5 |     5
   4 |     6

ここで、別の挿入を実行すると:

INSERT INTO table1 (col_b) VALUES(7);

次のエラーメッセージで失敗します。

エラー:重複するキー値が一意の制約「table1_pkey」に違反しています詳細:キー(uid)=(5)はすでに存在しています。

対照的に、MySQLは以下に示すようにこれを適切に処理します。

INSERT INTO table1 (uid, col_b) VALUES(4, 4);

次に、uidを設定せずに別の行を挿入します

INSERT INTO table1 (col_b) VALUES(3);

クエリは失敗せず、uidは5にジャンプします。

+-----+-------+
| uid | col_b |
+-----+-------+
|   1 |     1 |
|   2 |     2 |
|   4 |     4 |
|   5 |     3 |
+-----+-------+

Linux(x86_64)およびPostgreSQL 9.4.9のMySQL 5.6.33でテストが行​​われました


10
あなたは比較をしているのですが、ここには解決策はありません!答えですか?
Anwar、

4
@Anwarは、答えがシリアル/シーケンスを使用することであると述べているさまざまな答えを単に拡張します。これは、考慮すべき重要なコンテキストを提供します。
Programster

38

Postgres 10以降、SQL標準で定義されているID列もサポートされています。

create table foo 
(
  id integer generated always as identity
);

明示的に要求されない限りオーバーライドできないID列を作成します。次の挿入は、次のように定義された列で失敗しますgenerated always

insert into foo (id) 
values (1);

ただし、これは却下できます。

insert into foo (id) overriding system value 
values (1);

オプションを使用する場合、generated by defaultこれは基本的に既存のserial実装と同じ動作です。

create table foo 
(
  id integer generated by default as identity
);

値を手動で指定する場合、基になるシーケンスも手動で調整する必要があります- serial列の場合と同じです。


ID列は、デフォルトでは(serial列のように)主キーではありません。1つの場合は、主キー制約を手動で定義する必要があります。


26

古い質問を再ハッシュ化するために申し訳ありませんが、これはGoogleにポップアップした最初のスタックオーバーフローの質問/回答でした。

この投稿(Googleで最初に登場)は、PostgreSQL 10のより更新された構文の使用について説明しています。https//blog.2ndquadrant.com/postgresql-10-identity-columns/

それはたまたまです:

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);

それが役に立てば幸い:)


1
これは確かにPostgreSQL 10への移行方法であり、DB2やOracleなどの他のデータベースソフトウェアと同じ構文です。
アドリアン、

1
@adriaan実際、GENERATED … AS IDENTITYコマンドは標準SQLです。最初にSQL:2003で追加され、次にSQL:2008で明確化されました。機能#T174&F386&T178を参照してください。
バジルブルク

16

SERIALまたはシーケンスフィールドに直接挿入しないように注意する必要があります。そうしないと、シーケンスが挿入された値に達したときに書き込みが失敗します。

-- Table: "test"

-- DROP TABLE test;

CREATE TABLE test
(
  "ID" SERIAL,
  "Rank" integer NOT NULL,
  "GermanHeadword" "text" [] NOT NULL,
  "PartOfSpeech" "text" NOT NULL,
  "ExampleSentence" "text" NOT NULL,
  "EnglishGloss" "text"[] NOT NULL,
  CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
)
WITH (
  OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');


 INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');

 INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
           VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');

SELECT * from test; 

15

質問のコンテキストと@ sereja1cによるコメントへの返信では、作成SERIALによりシーケンスが暗黙的に作成されるため、上記の例では、

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEfoo_id_seqシリアル列のシーケンスを暗黙的に作成しますfoo.id。したがって、SERIAL[4バイト]は、IDに特定のデータ型が必要でない限り、使いやすさの点で優れています。



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