Postgresql SERIAL работает по-другому?
У меня есть таблица postgres с серийным идентификатором.
id (serial) name age
Вставка обычно происходит из веб-приложения.
Я вставил две новые записи вручную, установив id как max (id) +1****
После этих 2 вставок, когда веб-приложение вставляет 2 записи, выдает ошибку дублированного ключа.
Всего за 2 записи. После этого все работает нормально.
Вопрос в том, почему моя ручная вставка не увеличивает серийный номер?
Отличаются ли автоинкремент и серийный номер?
Что мне здесь не хватает? MySQL или любой другой SQL имеют такую же проблему?
3 ответа
Когда вы создаете serial
или жеbigserial
столбец, PostgreSQL на самом деле делает три вещи:
- Создает
int
или жеbigint
колонка. - Создает последовательность (принадлежит столбцу) для генерации значений для столбца.
- Устанавливает значение столбца по умолчанию для последовательности
nextval()
,
Когда вы ВСТАВЛЯЕТЕ значение без указания serial
столбец (или если вы явно указываете DEFAULT
как его стоимость), nextval
будет вызываться в последовательности:
- Вернуть следующее доступное значение для столбца.
- Увеличьте значение последовательности.
Если вы вручную указали значение не по умолчанию для serial
столбец, то последовательность не будет обновлена и nextval
может вернуть значения, которые ваш serial
колонка уже использует. Так что если вы делаете такие вещи, вам придется вручную исправить последовательность, вызвав nextval
или жеsetval
,
Также имейте в виду, что записи могут быть удалены, поэтому пробелы в serial
столбцы следует ожидать, так что с помощью max(id) + 1
не очень хорошая идея, даже если бы не было проблем с параллелизмом.
Если вы используете serial
или же bigserial
Лучше всего позволить PostgreSQL позаботиться о назначении значений для вас и притвориться, что это непрозрачные числа, которые просто появляются в определенном порядке: не присваивайте их себе и не думайте о них иначе чем уникальность. Это правило применимо ко всей базе данных IMO.
Я не уверен, как MySQL auto_increment
работает со всеми различными типами баз данных, но, возможно, хорошее руководство поможет.
Если вы хотите вставить запись в таблицу с serial
столбец - просто пропустите его из запроса - он будет сгенерирован автоматически.
Или вы можете вставить значение по умолчанию с помощью чего-то вроде:
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');
Я вставил две новые записи вручную, установив 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, ... .