Pdo, postgresql, lastinsertid

junior17

Новичок
Здравствуйте,

В документации pdo написано:

Возвращает ID последней вставленной строки или последнее значение от объекта последовательности, в зависимости от базового драйвера. Например, PDO_PGSQL требует задать имя объекта последовательности для параметра name.
Но у меня в postgre 9.6 $pdo->lastInsertId() без указания имени последовательности возвращает последнюю запись SEQUENCE.

Пример:

Код:
CREATE SEQUENCE users_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

CREATE TABLE users (
    id bigint DEFAULT nextval('users_id_seq'::regclass) NOT NULL,
    name character varying NOT NULL,
    PRIMARY KEY(id)
);

$pdo = $dbh->prepare("INSERT INTO users (name) VALUES(?)");
$pdo->bindValue(1, $name);
$pdo->execute();
$pdo->lastInsertId(); /* Возвращает последнего пользователя */
У меня в коде много где используется $pdo->lastInsertId(), поэтому немного настораживает, возможно ли то, что эта функция вернет не то, что требуется? Просьба разъяснить этот момент. Спасибо.
 
Последнее редактирование:

Adelf

Administrator
Команда форума
приучись сначала узнавать id а лишь потом создавать сущность. это даст некоторый профит в будущем.
 

AnrDaemon

Продвинутый новичок
Куда уж подробнее? Самый простой способ: создавай пустую сущность в БД, узнавай её Id, потом уже используй этот Id при сохранении настоящей сущности.
 

Adelf

Administrator
Команда форума
@AnrDaemon, а не проще просто узнавать следующее значение секвенса и создавать сущность с этим id?
 

AnrDaemon

Продвинутый новичок
Если это доступно - да. Так проще. При условии, что это узнанное значение гарантированно не будет кем-то использовано, пока я создаю сущность.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
приучись сначала узнавать id а лишь потом создавать сущность. это даст некоторый профит в будущем.
если я правильно тебя понимаю, ты советуешь делать сначала INSERT, потом ->lastInsertedId(), затем UPDATE
расскажи, пожалуйста, какой профит это приносит в рамках одного процесса
цена очевидна: UPDATE - относительно дорогая операция, и без специального проектирования приводит к дедлокам или race condition намного чаще, чем INSERT
 

AnrDaemon

Продвинутый новичок
Единственное "специальное проектирование", которое необходимо - явно избегать работы с пустыми сущностями в открытой части проекта. Только в админке/API, только с целью их перезаписи/удаления.
И как UPDATE может привести к дедлоку или гонке? Сущность уже создана, но она пустая и проект её не воспринимает и не ссылается на неё нигде.
 

Adelf

Administrator
Команда форума
если я правильно тебя понимаю, ты советуешь делать сначала INSERT, потом ->lastInsertedId(), затем UPDATE
расскажи, пожалуйста, какой профит это приносит в рамках одного процесса
цена очевидна: UPDATE - относительно дорогая операция, и без специального проектирования приводит к дедлокам или race condition намного чаще, чем INSERT
это @AnrDaemon советует.
Я советую узнать значение секвенса.

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

AnrDaemon

Продвинутый новичок
Я рад, что "это ж сиквенсы", вот только писал я это не для тех, кому это "ну это ж", а в общем случае.
 

fixxxer

К.О.
Партнер клуба
"В общем случае" сиквенсы прекрасно эмулируются через table sequences ( sequenceType, value ).

Ну или UUID вообще.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Первое.
Эмуляция через отдельную таблицу?
Узнать sequence, и использовать отдельно от записи?
В общем случае?

Дорогие мои! Вы советуете денормализацию в общем случае. Это с уверенностью можно назвать нарушением основных принципов проектирования СУБД. Никаких преимуществ подхода вы не описываете - следовательно, вы просто плодите сущности без необходимости. В общем случае!
Советуйте лучше Mongo - потому что web scale.

Вот UUID - лучшее решение в общем случае, да
 
Последнее редактирование:
  • Like
Реакции: WMix

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Второе.
Update приводит к race condition, если другая транзакция работает со списком сущностей. Если эта другая транзакция выполняет SELECT FOR UPDATE по диапазону (а как еще?) - несколько параллельных update-ов могут привести к дедлоку.

Третье.
явно избегать работы с пустыми сущностями в открытой части проекта. Только в админке/API, только с целью их перезаписи/удаления.
Таким образом, у нас появляется еще одно архитектурное ограничение, неявное соглашение, которое неприемлемо в сервисной архитектуре.
Преимуществ нет никаких, зато ограничений - целая пачка.
Герой костыля и монолита.
 

fixxxer

К.О.
Партнер клуба
В общем случае?
Под "общим случаем" я тут имел ввиду:
a) СУБД без поддержки сиквенсов,
б) ситуации, когда надо поддерживать общий сиквенс при шардинге по нескольким серверам (тут может быть даже и не таблица, а какой-нибудь счетчик в Redis).

Сам я предпочитаю UUID, но почему-то у многих к нему какая-то личная неприязнь, что ли.

Про dummy insert и последующий update обсуждать не хочу, я это тоже считаю ерундой :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@fixxxer, в разделе про постгрес "общий случай" с СУБД без поддержки сиквенсов, шардинга и полей UUID смотрится забавно. Не будем показывать пальцем )))
 

Фанат

oncle terrible
Команда форума
Ок, с идеей зачем-то инсерт+апдейт вместо просто инсерта разобрались. Кстати, про бенефиты мы так и не услышали

Мне еще интересно про узнавать значение сиквенса наперед и атомарность этой операции.
Я это так понял, что мы лезем в бд узнавать следующее значение сиквенса, создаем сучность и сохраняем ее, передавая в ид узнанное ранее значение.
Если я правильно понял, то я не понял, каким образом мы гарантированы от рейс кондишен.
 
Сверху