Postgresql SERIAL работает по-разному?

У меня есть таблица postgres с идентификатором SERIAL.

id (serial) name age

Вставка обычно происходит из веб-приложения.

Я вставил вручную две новые записи, устанавливающие id как max (id) +1 ****

После того, как эти 2 вставьте, когда веб-приложение вставляет 2 записи, они дают повторяющуюся ошибку ключа.

Только для 2 записей. После этого все работает нормально.

Вопрос: почему моя ручная вставка не увеличивала серийный номер?

Являются ли разные приращения и серийные номера?

Что мне здесь не хватает? У MySQL или любого другого SQL есть такая же проблема?

Ответ 1

Когда вы создаете столбец serial или bigserial, PostgreSQL фактически выполняет три функции:

  • Создает столбец int или bigint.
  • Создает последовательность (принадлежащую столбцу) для создания значений для столбца.
  • Устанавливает значение по умолчанию столбца в последовательность nextval().

Когда вы вставляете значение без указания столбца serial (или если вы явно указываете DEFAULT как его значение), nextval будет вызываться в последовательности:

  • Возвращает следующее доступное значение для столбца.
  • Увеличить значение последовательности.

Если вы вручную укажете значение по умолчанию для столбца serial, то последовательность не будет обновляться, а nextval может возвращать значения, которые уже используют столбец serial. Поэтому, если вы это сделаете, вам придется вручную исправить последовательность, вызвав nextval или setval.

Также имейте в виду, что записи могут быть удалены, поэтому следует ожидать пробелы в столбцах serial, поэтому использование max(id) + 1 не является хорошей идеей, даже если не было проблем с concurrency.

Если вы используете serial или bigserial, лучше всего позволить PostgreSQL позаботиться о присвоении вам значений и притвориться, что они непрозрачные цифры, которые только что выходят в определенном порядке: не назначайте их сами и не предполагайте ничего о них, кроме уникальности. Это правило применяется ко всей базе данных IMO.


Я не уверен, что MySQL auto_increment работает со всеми различными типами баз данных, но возможно прекрасное руководство поможет.

Ответ 2

Если вы хотите вставить запись в таблицу с столбцом serial - просто опустите ее из запроса - она ​​будет автоматически сгенерирована.

Или вы можете вставить его значение defaul с чем-то вроде:

insert into your_table(id, val)
values (default, '123');

Третий вариант - неправильно принимать значения из последовательной последовательности:

insert into your_table(id, val)
values (nextval(pg_get_serial_sequence('your_table','id')), '123');

Ответ 3

Я вставил вручную две новые записи, устанавливающие id как max (id) +1 **

Этот подход абсолютно неверен и не будет работать в любой базе данных. Это только работало для вас до сих пор в MySQL благодаря удачи.

Если одновременно запускаются два соединения, они получат одинаковый идентификатор. Он может работать только надежно, если вы заблокируете таблицу против одновременных чтений, чтобы только одно соединение могло получать идентификатор за раз.

Это также ужасно неэффективно.

Вот почему существуют последовательности, так что вы можете надежно получать идентификаторы в присутствии параллельных вставок.

Просто используйте:

INSERT INTO my_table(data1, data2) VALUES ('a','b') RETURNING id;

или

INSERT INTO my_table(id, data1, data2) VALUES (DEFAULT, 'a','b') RETURNING id;

DEFAULT является специальным держателем места, который сообщает базе данных, чтобы получить значение по умолчанию для этого столбца из определения таблицы. По умолчанию используется nextval('my_table_id_seq'), поэтому следующее значение последовательности будет вставлено.

Поскольку вы задаете основные вопросы о последовательностях, я рекомендую вам также учитывать, что последовательности не являются беспроблемными. Нормально для последовательностей иметь "дыры", где значения таблицы идут 1, 3, 4, 5, 9, 10,....