Предсказать следующий автоматически вставленный идентификатор строки (SQLite)

StackOverflow https://stackoverflow.com/questions/107005

  •  01-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь найти, есть ли надежный способ (используя SQLite), чтобы найти идентификатор следующей вставляемой строки, прежде чем он будет вставлен.Мне нужно использовать идентификатор для другого оператора вставки, но у меня нет возможности мгновенно вставить и получить следующую строку.

Неужели предсказать следующий идентификатор так же просто, как получить последний идентификатор и добавить его?Это гарантия?

Редактировать:Еще немного рассуждений...Я не могу вставить немедленно, потому что вставка может быть отменена пользователем.Пользователь внесет некоторые изменения, операторы SQL будут сохранены, и оттуда пользователь может либо сохранить (вставив все строки сразу), либо отменить (ничего не меняя).В случае сбоя программы желаемая функциональность заключается в том, чтобы ничего не менялось.

Это было полезно?

Решение

Либо отказ, либо одновременная фиксация серии операций с базой данных — это именно то, для чего нужны транзакции.Запрос BEGIN; прежде чем пользователь начнет возиться и COMMIT; как только он/она закончит.Вам гарантировано, что либо все изменения будут применены (если вы зафиксируете их), либо все будет отменено (если вы запросите ROLLBACK;, если программа выйдет из строя, отключится питание и т. д.).После чтения из базы данных вам также гарантируется, что данные в порядке до конца транзакции, поэтому вы можете получить MAX(id) или что угодно, не беспокоясь об условиях гонки.

http://www.sqlite.org/lang_transaction.html

Другие советы

Пытаться SELECT * FROM SQLITE_SEQUENCE WHERE name='TABLE';.Он будет содержать поле с именем seq это наибольшее число для выбранной таблицы.Добавьте 1 к этому значению, чтобы получить следующий идентификатор.

Также см. Статья об автоинкременте SQLite, откуда и взята приведенная выше информация.

Ваше здоровье!

Вероятно, вам удастся добавить 1 к значению, возвращаемому sqlite3_last_insert_rowid при определенных условиях, например, при использовании того же подключения к базе данных и отсутствии других одновременных записей.Конечно, вы можете обратиться к исходному коду sqlite, чтобы подтвердить свои предположения.

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

Вставьте строку с каким-либо флагом INVALID, получите идентификатор, при необходимости отредактируйте его, удалите при необходимости или пометьте как действительный.И не беспокойтесь о пробелах в последовательности.

Кстати, вам нужно будет придумать, как сделать недействительную часть самостоятельно.Маркировка чего-либо как NULL может сработать в зависимости от специфики.

Редактировать: Если можете, воспользуйтесь предложением Иви об использовании правильных транзакций.Это намного меньше работы.

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

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

В большинстве сред нельзя предполагать, что данные, полученные с помощью SELECT в рамках транзакции, являются повторяемыми.Например

SELECT Balance FROM Bank ...
UPDATE Bank SET Balance = valuefromselect + 1.00 WHERE ...

После UPDATE значение баланса вполне может быть изменено.Иногда вы можете обойти эту проблему, сначала обновив интересующую вас строку в банке в рамках транзакции, поскольку это гарантированно блокирует строку, предотвращая изменение ее значения в дальнейших обновлениях до завершения вашей транзакции.

Однако иногда лучший способ обеспечить согласованность в этом случае — проверить свои предположения о содержимом данных в предложении WHERE обновления и проверить количество строк в приложении.В приведенном выше примере, когда вы «UPDATE Bank», предложение WHERE должно предоставлять ожидаемое текущее значение баланса:

WHERE Balance = valuefromselect

Если ожидаемый баланс больше не соответствует, то и условие WHERE не выполняется — UPDATE ничего не делает, а rowcount возвращает 0.Это говорит о том, что возникла проблема с параллелизмом и вам необходимо повторно запустить операцию, если что-то еще не пытается одновременно изменить ваши данные.

select max(id) from particular_table is unreliable for the reason below..

http://www.sqlite.org/autoinc.html

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

Я думаю, что это невозможно сделать, потому что невозможно быть уверенным, что между вашим запросом и вставкой ничего не будет вставлено.(возможно, вы сможете заблокировать таблицу для вставок, но фу)

Кстати, я использовал только MySQL, но не думаю, что это будет иметь какое-то значение)

Скорее всего, вы сможете добавить +1 к самому последнему идентификатору.Я бы посмотрел на все (возвращаясь назад) существующие идентификаторы в упорядоченной таблице..Согласованы ли они и является ли идентификатор каждой строки на один больше, чем последний?Если да, то, вероятно, с вами все будет в порядке.Однако я бы оставил комментарии в коде, объясняющие это предположение.Выполнение блокировки поможет гарантировать, что при этом вы не получите дополнительных строк.

Выберите значение Last_insert_rowid().

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

select max(id) from particular_table;

Следующий идентификатор будет +1 от максимального идентификатора.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top