如何从SQLite表中检索最后一个自动递增的ID?


82

我有一个表Messages,其列ID(主键,自动递增)和Content(文本)。
我有一个表Users,具有用户名列(主键,文本)和哈希。
一个发件人(用户)将一条消息发送给许多收件人(用户),并且一个收件人(用户)可以有很多消息。
我创建了一个具有两列的表Messages_Recipients:MessageID(指的是Messages表的ID列和Recipient(指的是Users表中的username列)。该表代表了收件人和消息之间的多对多关系。

所以,我的问题是这个。将新消息的ID存储在数据库中后,将创建该ID。但是,如何保留对我刚刚添加的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);
}

我使用SQlite(ADO.Net版本)
Dabblernl 2010年

Answers:


88

使用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
再次感谢你!不幸的是,它不起作用,因为在关闭用于更新DataTable的连接之前需要调用last_insert_rowid()函数。尽管这可能是SQLite的怪癖...
Dabblernl 2010年

1
抱歉,是的。您是否在messagesAdapter.Update(messages);之后尝试执行它?
MikeW 2010年

2
@Dabblernl似乎另一个答案更可靠,如果您认为另一个答案更有用,则可能需要更改已接受的答案
MikeW 2014年

但是RowId并不总是与PRIMARY KEY相同。除了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

好答案。奇怪的是,如果从“ sqlite_sequence”中删除一行,则在“ DB Browser for SQLite”中,您将不再获得某个表的最后一个ID。
CoolMind

8

SELECT last_insert_rowid()在多线程环境中使用时遇到了问题。如果另一个线程插入具有autoinc的另一个表中,则last_insert_rowid将返回新表中的autoinc值。

他们在文档中声明了以下内容:

如果在运行sqlite3_last_insert_rowid()函数时一个单独的线程在同一数据库连接上执行新的INSERT操作,从而更改了最后一个插入的rowid,则sqlite3_last_insert_rowid()返回的值是不可预测的,并且可能不等于旧的或新的last插入rowid。

那是来自sqlite.org doco


1
我猜您可以为每个线程使用单独的连接,但是我注意到它对性能有很大的影响。在我的方案中,我每秒只能执行约15次插入。
菲德尔(Fidel)2010年

我的猜测是,单独的线程将是同一问题。不幸的是,到目前为止似乎还没有线程安全的工作,我的直觉是多个事务甚至还不够...所以要小心:|
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获取最后插入行ID,还有另一个查询:

SELECT rowid from your_table_name order by ROWID DESC limit 1

1
此选项会因发现行号之前执行排序算法而导致性能下降
Sr. Libre

2
也许它不是最佳选择,但实际上它在关闭并重新打开连接后才起作用,而其他两个解决方案对我而言却不是。
Francine DeGrood Taylor

以下变体避免使用ORDER BY。应该更快,但是我没有测试:SELECT MAX(rowid) FROM your_table_name
tanius
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.