Как получить последний автоинкрементный идентификатор из таблицы SQLite?

85

У меня есть таблица сообщений с идентификатором столбцов (первичный ключ, автоинкремент) и содержимым (текст).
У меня есть таблица Users с именем пользователя столбцов (первичный ключ, текст) и Hash.
Сообщение отправляется одним Отправителем (пользователем) нескольким получателям (пользователям), и у получателя (пользователя) может быть много сообщений.
Я создал таблицу Messages_Recipients с двумя столбцами: MessageID (со ссылкой на столбец ID таблицы Messages и Recipient (со ссылкой на столбец имени пользователя в таблице Users). Эта таблица представляет отношение «многие ко многим» между получателями и сообщениями.

Итак, у меня такой вопрос. Идентификатор нового сообщения будет создан после того, как он будет сохранен в базе данных. Но как я могу сохранить ссылку на MessageRow, которую я только что добавил, чтобы получить этот новый MessageID?
Конечно, я всегда могу найти в базе данных последнюю добавленную строку, но это может вернуть другую строку в многопоточной среде?

РЕДАКТИРОВАТЬ: Насколько я понимаю, для SQLite вы можете использовать расширение SELECT last_insert_rowid(). Но как мне вызвать это утверждение из ADO.Net?

Мой код сохранения состояния (сообщения и получатели сообщений - это таблицы данных):

public void Persist(Message message)
{
    pm_databaseDataSet.MessagesRow messagerow;
    messagerow=messages.AddMessagesRow(message.Sender,
                            message.TimeSent.ToFileTime(),
                            message.Content,
                            message.TimeCreated.ToFileTime());
    UpdateMessages();
    var x = messagerow;//I hoped the messagerow would hold a
    //reference to the new row in the Messages table, but it does not.
    foreach (var recipient in message.Recipients)
    {
        var row = messagesRecipients.NewMessages_RecipientsRow();
        row.Recipient = recipient;
        //row.MessageID= How do I find this??
        messagesRecipients.AddMessages_RecipientsRow(row);
        UpdateMessagesRecipients();//method not shown
    } 

}

private void UpdateMessages()
{
    messagesAdapter.Update(messages);
    messagesAdapter.Fill(messages);
}
Dabblernl
источник
Я использую SQlite (версия
ADO.Net
stackoverflow.com/questions/531109/…
Маурисио Шеффер,

Ответы:

89

В SQL Server вы должны ВЫБРАТЬ SCOPE_IDENTITY (), чтобы получить последнее значение идентификатора для текущего процесса.

С SQlite похоже, что для автоинкремента вы бы сделали

SELECT last_insert_rowid()

сразу после вставки.

http://www.mail-archive.com/sqlite-users@sqlite.org/msg09429.html

В ответ на ваш комментарий, чтобы получить это значение, вы хотели бы использовать код SQL или OleDb, например:

using (SqlConnection conn = new SqlConnection(connString))
{
    string sql = "SELECT last_insert_rowid()";
    SqlCommand cmd = new SqlCommand(sql, conn);
    conn.Open();
    int lastID = (Int32) cmd.ExecuteScalar();
}
MikeW
источник
2
Еще раз спасибо! К сожалению, это не работает, так как функция last_insert_rowid () должна быть вызвана до закрытия соединения, которое используется для обновления DataTable. Хотя это может быть причудой SQLite ...
Dabblernl
1
Извините, да, вероятно, это правда. Вы пытались запустить его после messagesAdapter.Update (messages);
MikeW
2
@Dabblernl кажется, что другой ответ более надежен, вы можете изменить принятый ответ, если чувствуете, что другой более полезен
MikeW
Но RowId не всегда совпадает с PRIMARY KEY. ExceptIf the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.
Кое Кто
110

Еще один вариант - посмотреть системную таблицу sqlite_sequence. Ваша база данных sqlite будет иметь эту таблицу автоматически, если вы создали любую таблицу с первичным ключом автоинкремента. Эта таблица предназначена для sqlite, чтобы отслеживать поле автоинкремента, чтобы он не повторял первичный ключ даже после удаления некоторых строк или после сбоя какой-либо вставки (подробнее об этом здесь http://www.sqlite.org/autoinc .html ).

Таким образом, эта таблица дает дополнительное преимущество: вы можете узнать первичный ключ только что вставленного элемента даже после того, как вы вставили что-то еще (в других таблицах, конечно!). Убедившись, что ваша вставка прошла успешно (иначе вы получите ложное число), вам просто нужно сделать:

select seq from sqlite_sequence where name="table_name"
полиглот
источник
1
Работает при увеличении последовательности и после удаления строки.
Марек Бар
Хороший ответ. Странно, что в «Браузере БД для SQLite», если удалить строку из «sqlite_sequence», вы больше не получите последний идентификатор для какой-то таблицы.
CoolMind
9

У меня были проблемы с использованием SELECT last_insert_rowid() в многопоточной среде. Если другой поток вставляет в другую таблицу, которая имеет autoinc, last_insert_rowid вернет значение autoinc из новой таблицы.

Вот где они заявляют об этом в документе:

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

Это из sqlite.org doco

Фидель
источник
1
Я думаю, вы можете использовать отдельное соединение для каждого потока, но я заметил, что это сильно снижает производительность. В моем сценарии я могу делать только около 15 вставок в секунду.
Фидель
Я предполагаю, что отдельные потоки будут той же проблемой. К сожалению, на сегодняшний день, похоже, не существует поточно-ориентированной работы, я подозреваю, что нескольких транзакций будет недостаточно ... так что будьте осторожны: |
rogerdpack
5

Пример кода из решения @polyglot

SQLiteCommand sql_cmd;
sql_cmd.CommandText = "select seq from sqlite_sequence where name='myTable'; ";
int newId = Convert.ToInt32( sql_cmd.ExecuteScalar( ) );
О'Нил
источник
4

Согласно Android Sqlite получить последний идентификатор строки вставки, есть еще один запрос:

SELECT rowid from your_table_name order by ROWID DESC limit 1
CoolMind
источник
1
Эта опция имеет
старший Либре
2
Возможно, это менее оптимально, но оно действительно работает после закрытия и повторного открытия соединения, а два других решения не для меня.
Francine DeGrood Taylor
Следующий вариант избегает ORDER BY. Должно быть быстрее, но я этого не тестировал:SELECT MAX(rowid) FROM your_table_name
tanius