Auto-incrémentation PostgreSQL

579

Je passe de MySQL à PostgreSQL et je me demandais comment je peux faire des valeurs d'auto-incrémentation. J'ai vu dans les documents PostgreSQL un type de données "série", mais j'obtiens des erreurs de syntaxe lors de son utilisation (en v8.0).

Ian
la source
9
si vous fournissez la requête et l'erreur que vous obtenez - peut-être que quelqu'un pourrait vous dire quel est le problème avec la requête.
2
Mon premier hit trop Mich 'et comme c'est une question qui obtient suffisamment de vues pour être pertinente, pourquoi ne pas la voter. PS ce n'est pas anodin si vous ne savez pas comment le faire.
baash05
1
SERIAL est le choix préféré si votre pilote client est Npgsql. Le fournisseur sélectionne en interne de nouvelles valeurs après un INSERT à l'aide de SELECT currval (pg_get_serial_sequence ('table', 'column')). Cela échouera si la colonne sous-jacente n'est pas de type série (type numérique + séquence explicite par exemple)
Olivier MATROT
Juste pour la curiosité ... Pourquoi quelqu'un doit-il migrer de MySQL qui est très bien, vers PostgreSql?
villamejia
17
... ce qui est encore mieux.
Rohmer

Réponses:

702

Oui, SERIAL est la fonction équivalente.

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 est juste une macro de création d'heure de table autour de séquences. Vous ne pouvez pas modifier SERIAL sur une colonne existante.

Trey
la source
19
citer le nom de la table est une très mauvaise pratique
Evan Carroll
71
La citation des noms de table est une habitude car j'ai hérité d'une base de données qui avait des noms de casse mixtes et la citation des noms de table est une condition d'utilisation.
Trey
26
@Evan Carroll - Pourquoi est-ce une mauvaise habitude (juste demander)?
Christian
27
car à moins que vous ayez une table "Table", "table"puis ne la laissez pas entre guillemets et canonisez-la table. La convention est simplement de ne jamais utiliser de guillemets dans Pg. Vous pouvez, si vous le souhaitez, utiliser des noms de casse mixtes pour l'apparence, mais ne l'exigez pas: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;cela fonctionnera, tout comme SELECT * FROM foobar.
Evan Carroll
26
Par doc postgres, soit de manière cohérente, soit de non-citation: postgresql.org/docs/current/interactive/…
Καrτhικ
225

Vous pouvez utiliser tout autre type de données entier , tel que smallint.

Exemple :

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;

Mieux vaut utiliser votre propre type de données plutôt que le type de données série utilisateur .

Ahmad
la source
11
Je dirais que c'est en fait la meilleure réponse car cela m'a permis de modifier une table que je venais de créer dans PostgreSQL en définissant les colonnes par défaut (après avoir lu sur CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . CEPENDANT, je ne sais pas trop pourquoi vous avez changé de propriétaire.
JayC
12
@JayC: D'après la documentation : Enfin, la séquence est marquée comme "détenue par" la colonne, de sorte qu'elle sera supprimée si la colonne ou la table est supprimée.
user272735
9
pourquoi la communauté postgres ne réinvente-t-elle pas simplement le mot clé d'auto-incrémentation?
Dr Deo
2
@Dr Deo: ils utilisent un mot-clé de série à la place de l'auto-incrémentation, je ne sais pas pourquoi :)
Ahmad
4
Il y a aussi smallserial si vous voulez juste un type de données plus petit.
beldaz
110

Si vous souhaitez ajouter une séquence à id dans le tableau qui existe déjà, vous pouvez utiliser:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
sereja
la source
Qu'est-ce que la séquence? Où est AUTO_INCREMENT?
Vert
23
@Green: AUTO_INCREMENT ne fait pas partie du standard SQL, il est spécifique à MySQL. Les séquences sont quelque chose qui fait un travail similaire dans PostgreSQL.
beldaz
5
si vous utilisez 'id SERIAL', il créera automatiquement une séquence dans PostgreSQL. Le nom de cette séquence sera <nom de la table> _ <nom de la colonne> _seq
Jude Niroshan
Ne devez-vous pas utiliser ALTER COLUMN user_id?
Alec
J'ai essayé cette méthode mais j'obtiens une erreur: des ERROR: syntax error at or near "DEFAULT"suggestions?
Ely Fialkoff
44

Bien qu'il semble que les séquences soient l' équivalent de l'auto-incrémentation de MySQL, il existe quelques différences subtiles mais importantes:

1. Échec des requêtes Incrémenter la séquence / série

La colonne série est incrémentée en cas d'échec des requêtes. Cela entraîne une fragmentation des requêtes ayant échoué, et pas seulement des suppressions de lignes. Par exemple, exécutez les requêtes suivantes sur votre base de données 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;

Vous devriez obtenir la sortie suivante:

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

Remarquez comment l'uid passe de 1 à 3 au lieu de 1 à 2.

Cela se produit toujours si vous deviez créer manuellement votre propre séquence avec:

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;

Si vous souhaitez tester en quoi MySQL est différent, exécutez ce qui suit sur une base de données 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);

Vous devriez obtenir ce qui suit sans frustration :

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

2. La définition manuelle de la valeur de la colonne série peut entraîner l'échec des requêtes futures.

Cela a été souligné par @trev dans une réponse précédente.

Pour simuler cela, définissez manuellement l'uid sur 4 qui "se heurtera" plus tard.

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

Données du tableau:

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

Exécutez un autre insert:

INSERT INTO table1 (col_b) VALUES(6);

Données du tableau:

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

Maintenant, si vous exécutez une autre insertion:

INSERT INTO table1 (col_b) VALUES(7);

Il échouera avec le message d'erreur suivant:

ERREUR: la valeur de clé en double viole la contrainte unique "table1_pkey" DÉTAIL: la clé (uid) = (5) existe déjà.

En revanche, MySQL gérera cela avec élégance comme indiqué ci-dessous:

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

Insérez maintenant une autre ligne sans définir d'uid

INSERT INTO table1 (col_b) VALUES(3);

La requête n'échoue pas, uid passe simplement à 5:

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

Les tests ont été effectués sur MySQL 5.6.33, pour Linux (x86_64) et PostgreSQL 9.4.9

Programster
la source
10
Vous donnez une comparaison mais je ne vois aucune solution ici! Est-ce une réponse?
Anwar
4
@Anwar, il étend simplement les différentes réponses qui indiquent que la réponse est d'utiliser une série / séquence. Cela fournit un contexte important à prendre en considération.
Programster le
39

À partir de Postgres 10, les colonnes d'identité définies par la norme SQL sont également prises en charge:

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

crée une colonne d'identité qui ne peut pas être remplacée sauf demande explicite. L'insertion suivante échouera avec une colonne définie comme generated always:

insert into foo (id) 
values (1);

Cela peut cependant être annulé:

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

Lorsque vous utilisez l'option, generated by defaultc'est essentiellement le même comportement que l' serialimplémentation existante :

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

Lorsqu'une valeur est fournie manuellement, la séquence sous-jacente doit également être ajustée manuellement - la même chose qu'avec une serialcolonne.


Une colonne d'identité n'est pas une clé primaire par défaut (tout comme une serialcolonne). S'il doit en être un, une contrainte de clé primaire doit être définie manuellement.

un cheval sans nom
la source
26

Désolé de reformuler une vieille question, mais ce fut la première question / réponse Stack Overflow apparue sur Google.

Cet article (qui est apparu en premier sur Google) parle de l'utilisation de la syntaxe la plus mise à jour pour PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

qui se trouve être:

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

J'espère que cela pourra aider :)

Zhao Li
la source
1
C'est en effet la voie à suivre dans PostgreSQL 10 et c'est la même syntaxe que d'autres logiciels de base de données comme DB2 ou Oracle.
adriaan
1
@adriaan En fait, les GENERATED … AS IDENTITYcommandes sont du SQL standard. D'abord ajouté dans SQL: 2003 , puis clarifié dans SQL: 2008 . Voir caractéristiques # T174 & F386 & T178.
Basil Bourque
16

Vous devez faire attention à ne pas insérer directement dans votre champ SERIAL ou séquence, sinon votre écriture échouera lorsque la séquence atteindra la valeur insérée:

-- 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; 
trev
la source
15

Dans le cadre de la question posée et en réponse au commentaire de @ sereja1c, créer SERIALimplicitement crée des séquences, donc pour l'exemple ci-dessus-

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEcréerait implicitement une séquence foo_id_seqpour la colonne série foo.id. Par conséquent, SERIAL[4 octets] est bon pour sa facilité d'utilisation, sauf si vous avez besoin d'un type de données spécifique pour votre identifiant.

Prince
la source
3

Cette méthode fonctionnera à coup sûr, j'espère que cela aidera:

CREATE TABLE fruits(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL
);

INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');

or

INSERT INTO fruits VALUES(DEFAULT,'apple');

Vous pouvez vérifier ces détails dans le lien suivant: http://www.postgresqltutorial.com/postgresql-serial/

webtechnelson
la source
3

Depuis PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Sergey Vishnevetskiy
la source