PostgreSQL Autoincrement

579

Estoy cambiando de MySQL a PostgreSQL y me preguntaba cómo puedo hacer valores de autoincremento. Vi en los documentos de PostgreSQL un tipo de datos "serial", pero recibo errores de sintaxis cuando lo uso (en v8.0).

Ian
fuente
99
si proporciona la consulta y el error que está recibiendo, tal vez alguien podría decirle qué tiene de malo la consulta.
2
Mi primer golpe también fue Mich 'y como es una pregunta que tiene suficientes puntos de vista para ser relevante, ¿por qué no votarla? PD: no es trivial si no sabes cómo hacerlo.
baash05
1
SERIAL es la opción preferida si su controlador de cliente es Npgsql. El proveedor está seleccionando internamente nuevos valores después de un INSERT utilizando el método SELECT (pg_get_serial_sequence ('table', 'column')). Esto fallará si la columna subyacente no es de tipo serie (tipo numérico + secuencia explícita, por ejemplo)
Olivier MATROT
Solo por curiosidad ... ¿Por qué alguien tiene que migrar de MySQL, que es muy bueno, a PostgreSql?
villamejia
17
... que es aún mejor.
Rohmer

Respuestas:

702

Sí, SERIAL es la función equivalente.

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 es solo una macro de tiempo de creación de tabla alrededor de secuencias. No puede modificar SERIAL en una columna existente.

Trey
fuente
19
citar el nombre de la tabla es una práctica realmente mala
Evan Carroll
71
Citar los nombres de las tablas es un hábito, ya que heredé una base de datos que tenía nombres de mayúsculas y minúsculas, y es un requisito de uso citar los nombres de las tablas.
Trey
26
@Evan Carroll - ¿Por qué es un mal hábito (solo preguntando)?
Christian
27
porque a menos que tengas una tabla "Table"y "table"luego la dejes sin comillas y la canonice table. La convención es simplemente nunca usar comillas en Pg. Puede, si lo desea, usar nombres de mayúsculas y minúsculas para la apariencia, simplemente no lo requiere: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;funcionará, como lo hará SELECT * FROM foobar.
Evan Carroll
26
Por documento de postgres, cite o no entre comillas: postgresql.org/docs/current/interactive/…
Καrτhικ
225

Puede usar cualquier otro tipo de datos enteros , como smallint.

Ejemplo:

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;

Es mejor usar su propio tipo de datos, en lugar del tipo de datos en serie del usuario .

Ahmad
fuente
11
Diría que esta es la mejor respuesta porque me permitió modificar una tabla que acababa de crear en PostgreSQL configurando las columnas por defecto (después de leer en CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ) . SIN EMBARGO, no estoy muy seguro de por qué cambiaste de dueño.
JayC
12
@ JayC: de la documentación : por último, la secuencia se marca como "propiedad de" la columna, por lo que se descartará si se descarta la columna o la tabla.
user272735
99
¿Por qué la comunidad de Postgres no reinventa la palabra clave de autoincremento?
Dr. Deo
2
@Dr Deo: usan la palabra clave serial en lugar de autoincremento, no sé por qué :)
Ahmad
44
También hay una serie pequeña si solo desea un tipo de datos más pequeño.
beldaz
110

Si desea agregar una secuencia al id en la tabla que ya existe, puede usar:

CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
sereja
fuente
¿Qué es la secuencia? ¿Dónde está AUTO_INCREMENT?
Verde
23
@Green: AUTO_INCREMENT no es parte del estándar SQL, es específico de MySQL. Las secuencias son algo que hace un trabajo similar en PostgreSQL.
beldaz
55
si usa 'id SERIAL', creará automáticamente una secuencia en PostgreSQL. El nombre de esa secuencia será <nombre de tabla> _ <nombre de columna> _seq
Jude Niroshan
¿No tienes que usar ALTER COLUMN user_id?
Alec
Intenté este método pero recibo un error: ¿ ERROR: syntax error at or near "DEFAULT"Alguna sugerencia?
Ely Fialkoff
44

Si bien parece que las secuencias son equivalentes al aumento automático de MySQL, hay algunas diferencias sutiles pero importantes:

1. Las consultas fallidas incrementan la secuencia / serie

La columna serial se incrementa en consultas fallidas. Esto conduce a la fragmentación de consultas fallidas, no solo a la eliminación de filas. Por ejemplo, ejecute las siguientes consultas en su base de datos 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;

Debería obtener el siguiente resultado:

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

Observe cómo uid va de 1 a 3 en lugar de 1 a 2.

Esto todavía ocurre si tuviera que crear manualmente su propia secuencia con:

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 desea probar cómo es diferente MySQL, ejecute lo siguiente en una base de datos 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);

Debería obtener lo siguiente sin fragmentación :

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

2. La configuración manual del valor de la columna de serie puede provocar que las consultas futuras fallen.

Esto fue señalado por @trev en una respuesta anterior.

Para simular esto manualmente, configure el uid en 4, que "chocará" más tarde.

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

Datos de la tabla:

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

Ejecute otra inserción:

INSERT INTO table1 (col_b) VALUES(6);

Datos de la tabla:

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

Ahora, si ejecuta otra inserción:

INSERT INTO table1 (col_b) VALUES(7);

Fallará con el siguiente mensaje de error:

ERROR: el valor de clave duplicada viola la restricción única "table1_pkey" DETALLE: La clave (uid) = (5) ya existe.

En contraste, MySQL manejará esto con gracia como se muestra a continuación:

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

Ahora inserte otra fila sin configurar uid

INSERT INTO table1 (col_b) VALUES(3);

La consulta no falla, uid solo salta a 5:

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

Las pruebas se realizaron en MySQL 5.6.33, para Linux (x86_64) y PostgreSQL 9.4.9

Programador
fuente
10
¡Estás haciendo una comparación pero no veo ninguna solución aquí! ¿Es una respuesta?
Anwar
44
@Anwar simplemente extiende las diversas respuestas que indican que la respuesta es usar una serie / secuencia. Esto proporciona un contexto importante a tener en cuenta.
Programador
39

Comenzando con Postgres 10, las columnas de identidad según lo definido por el estándar SQL también son compatibles:

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

crea una columna de identidad que no se puede anular a menos que se solicite explícitamente. La siguiente inserción fallará con una columna definida como generated always:

insert into foo (id) 
values (1);

Sin embargo, esto se puede anular:

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

Cuando se usa la opción, generated by defaulteste es esencialmente el mismo comportamiento que la serialimplementación existente :

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

Cuando se suministra un valor manualmente, la secuencia subyacente también debe ajustarse manualmente, lo mismo que con una serialcolumna.


Una columna de identidad no es una clave principal de forma predeterminada (al igual que una serialcolumna). Si debe ser uno, una restricción de clave principal debe definirse manualmente.

un caballo sin nombre
fuente
26

Lo sentimos, para repetir una pregunta anterior, pero esta fue la primera pregunta / respuesta de Stack Overflow que apareció en Google.

Esta publicación (que apareció primero en Google) habla sobre el uso de la sintaxis más actualizada para PostgreSQL 10: https://blog.2ndquadrant.com/postgresql-10-identity-columns/

que resulta ser:

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

Espero que ayude :)

Zhao Li
fuente
1
De hecho, este es el camino a seguir en PostgreSQL 10 y es la misma sintaxis que otro software de base de datos como DB2 u Oracle.
adriaan
1
@adriaan En realidad, los GENERATED … AS IDENTITYcomandos son SQL estándar. Primero agregado en SQL: 2003 , luego aclarado en SQL: 2008 . Ver características # T174 y F386 y T178.
Basil Bourque
16

Debe tener cuidado de no insertar directamente en su SERIE o campo de secuencia, de lo contrario su escritura fallará cuando la secuencia alcance el valor insertado:

-- 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
fuente
15

En el contexto de la pregunta formulada y en respuesta al comentario de @ sereja1c, la creación SERIALimplícita crea secuencias, por lo que para el ejemplo anterior:

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEcrearía implícitamente una secuencia foo_id_seqpara la columna serial foo.id. Por lo tanto, SERIAL[4 Bytes] es bueno por su facilidad de uso a menos que necesite un tipo de datos específico para su identificación.

Príncipe
fuente
3

De esta manera funcionará seguro, espero que ayude:

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

Puede consultar los detalles en el siguiente enlace: http://www.postgresqltutorial.com/postgresql-serial/

webtechnelson
fuente
3

Desde PostgreSQL 10

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