Як отримати останній автоматично збільшений ідентифікатор з таблиці SQLite?


85

У мене є таблиця Повідомлення з ідентифікаторами стовпців (первинний ключ, автоінкремент) та вмістом (текст).
У мене є таблиця Користувачі зі стовпцями ім'я користувача (первинний ключ, текст) і хеш.
Повідомлення надсилається одним Відправником (користувачем) багатьом одержувачам (користувач), і одержувач (користувач) може мати багато повідомлень.
Я створив таблицю Messages_Recipients з двома стовпцями: MessageID (посилаючись на стовпець ID таблиці Messages та Recipient (маючи на увазі стовпець з іменем користувача в таблиці Користувачі). Ця таблиця представляє відношення "багато до багатьох" між одержувачами та повідомленнями.

Отже, питання у мене таке. Ідентифікатор нового повідомлення буде створений після того, як його буде збережено в базі даних. Але як я можу зберігати посилання на щойно доданий MessageRow для отримання цього нового ідентифікатора MessageID?
Я завжди можу шукати в базі даних останній доданий рядок, звичайно, але це може повернути інший рядок у багатопотоковому середовищі?

EDIT: Як я розумію, для SQLite ви можете використовувати SELECT last_insert_rowid(). Але як мені назвати це твердження з ADO.Net?

Мій код постійності (повідомлення та повідомленняRecipients - це таблиці даних):

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);
}

Я використовую SQlite (версія ADO.Net)
Dabblernl

Відповіді:


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();
}

2
Ще раз дякую вам! На жаль, це не працює, оскільки функцію last_insert_rowid () потрібно викликати перед тим, як з'єднання, яке використовується для оновлення DataTable, закрито. Це, можливо,
химерність

1
Вибачте, так це, мабуть, правда. Ви намагалися виконати його після messagesAdapter.Update (повідомлення);
MikeW

2
@Dabblernl, здається, інша відповідь є більш надійною, можливо, ви захочете змінити прийняту відповідь, якщо вважаєте, що інша є більш корисною
MikeW

Але RowId не завжди збігається з ОСНОВНИМ КЛЮЧОМ. КрімIf 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
Це працює, коли послідовність збільшується та після видалення рядка.
Marek Bar

Хороша відповідь. Дивно, що у "Браузері DB для SQLite", якщо видалити рядок із "sqlite_sequence", ви більше не отримуватимете останній ідентифікатор для якоїсь таблиці.
CoolMind

9

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

Ось де вони заявляють, що в документі:

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

Це з sqlite.org doco


1
Думаю, ви могли б використовувати окреме підключення для кожного потоку, але я помітив, що він має великий показник продуктивності. За моїм сценарієм я можу робити лише 15 вставок в секунду.
Fidel

Я припускаю, що окремі теми будуть однією проблемою. На жаль, на сьогоднішній день, здається, не існує поточно безпечної роботи, на мою думку, багаторазових транзакцій навіть не вистачить ... тому будьте обережні: |
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

1
Цей параметр передбачає покарання за продуктивність, завдяки якому слід виконати алгоритм сортування перед тим, як знайти роудіда
старший Лібре

2
Можливо, це менш оптимально, але насправді це працює після того, як з'єднання було закрито та знову відкрито, а два інших рішення для мене не зробили.
Франсін Дегруд Тейлор,

Наступний варіант дозволяє уникнути ORDER BY. Має бути швидше, але я цього не SELECT MAX(rowid) FROM your_table_name
тестував
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.