PostgreSQL Автоинкремент

579

Я переключаюсь с MySQL на PostgreSQL и мне было интересно, как я могу делать автоинкрементные значения. В документации PostgreSQL я видел тип данных "serial", но при его использовании я получаю синтаксические ошибки (в v8.0).

Ян
источник
9
если вы предоставите запрос и полученную ошибку - возможно, кто-то скажет вам, что не так с запросом.
2
Мой первый хит тоже Mich ', и, поскольку это вопрос, который получает достаточно мнений, чтобы быть актуальным, почему бы не проголосовать. PS Это не тривиально, если вы не знаете, как это сделать.
baash05
1
SERIAL является предпочтительным выбором, если вашим клиентским драйвером является Npgsql. Поставщик внутренне выбирает новые значения после INSERT, используя SELECT currval (pg_get_serial_sequence ('table', 'column')). Это не удастся, если нижележащий столбец не относится к типу serial (например, числовой тип + явная последовательность)
Оливье МАТРОТ
Просто для любопытства ... Почему кто-то должен перейти с MySQL, что очень хорошо, на PostgreSql?
Villamejia
17
... что еще лучше.
Ромер

Ответы:

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
Заключение в кавычки имен таблиц является привычкой, поскольку я унаследовал БД со смешанными именами, а цитирование имен таблиц - это требование использования.
Trey
26
@ Эван Кэрролл - Почему это плохая привычка (просто спрашиваю)?
Кристиан
27
потому что, если у вас нет стола, "Table"а "table"затем просто оставить его без кавычек и канонизировать его table. Соглашение просто никогда не использовать кавычки в Pg. Вы можете, если хотите, использовать смешанные имена падежей для внешнего вида, просто не требуйте этого: CREATE TABLE fooBar ( .. ); SELECT * FROM fooBar;будет работать, как и будет SELECT * FROM foobar.
Эван Кэрролл
26
За postgres doc, либо последовательно цитируйте, либо цитируйте: 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;

Лучше использовать свой собственный тип данных, а не пользовательский серийный тип данных .

Ahmad
источник
11
Я бы сказал, что на самом деле это лучший ответ, поскольку он позволил мне изменить таблицу, которую я только что создал в PostgreSQL, установив столбцы по умолчанию (после прочтения на CREATE SEQUENCE postgresql.org/docs/8.1/interactive/sql-createsequence.html ). , ОДНАКО, я не совсем уверен, почему вы сменили владельца.
JayC
12
@JayC: Из документации : Наконец, последовательность помечается как «принадлежащая» столбцу, поэтому она будет отброшена, если столбец или таблица будут удалены.
user272735
9
почему сообщество postgres просто не изобретает ключевое слово autoincrement?
Доктор Део
2
@Dr Deo: вместо ключевого слова autoincrement они используют серийный номер, я не знаю почему :)
Ахмад,
4
Есть также smallserial, если вы просто хотите меньший тип данных.
Бельдаз
110

Если вы хотите добавить последовательность к идентификатору в таблице, которая уже существует, вы можете использовать:

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.
Бельдаз
5
если вы используете 'id SERIAL', он автоматически создаст последовательность в PostgreSQL. Имя этой последовательности будет <имя таблицы> _ <имя столбца> _seq
Джуд Нирошан
Вам не нужно использовать ALTER COLUMN user_id?
Алек
Я пробовал этот метод, но я получаю ошибку: ERROR: syntax error at or near "DEFAULT"какие-либо предложения?
Ely Fialkoff
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 до 3 вместо 1 до 2.

Это все еще происходит, если вы должны были вручную создать свою собственную последовательность с:

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 |
+-----+-------+

Тестирование проводилось на MySQL 5.6.33, для Linux (x86_64) и PostgreSQL 9.4.9.

Programster
источник
10
Вы даете сравнение, но я не вижу здесь никакого решения! Это ответ?
Анвар
4
@ Анвара просто расширяет различные ответы, в которых говорится, что ответ должен использовать сериал / последовательность. Это обеспечивает некоторый важный контекст, который необходимо учитывать.
Programster
39

Начиная с Postgres 10, также поддерживаются столбцы идентификаторов, определенные стандартом SQL:

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

создает столбец идентификаторов, который не может быть переопределен без явного запроса. Следующая вставка завершится ошибкой со столбцом, определенным как 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столбца.


Столбец идентификации не является первичным ключом по умолчанию (как serialстолбец). Если это так, ограничение первичного ключа необходимо определить вручную.

a_horse_with_no_name
источник
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.
adriaan
1
@adriaan На самом деле GENERATED … AS IDENTITYкоманды являются стандартным SQL. Сначала добавлено в SQL: 2003 , затем уточнено в SQL: 2008 . Смотрите особенности # T174 & F386 & T178.
Василий Бурк
16

Вы должны быть осторожны, чтобы не вставить непосредственно в поле SERIAL или sequence, иначе ваша запись не будет выполнена, когда последовательность достигнет вставленного значения:

-- 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
источник
15

В контексте заданного вопроса и в ответ на комментарий @ sereja1c создание SERIALнеявно создает последовательности, поэтому для приведенного выше примера:

CREATE TABLE foo (id SERIAL,bar varchar);

CREATE TABLEбудет неявно создавать последовательность foo_id_seqдля последовательного столбца foo.id. Следовательно, SERIAL[4 байта] хороша для простоты использования, если вам не нужен конкретный тип данных для вашего идентификатора.

принц
источник
3

Этот способ будет работать наверняка, надеюсь, он поможет:

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

Вы можете проверить это в следующей ссылке: http://www.postgresqltutorial.com/postgresql-serial/

webtechnelson
источник
3

Начиная с PostgreSQL 10

CREATE TABLE test_new (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    payload text
);
Сергей Вишневецкий
источник