91

我有一个带有列 ID(主键、自动增量)和内容(文本)的消息表。
我有一个包含用户名(主键、文本)和哈希列的用户表。
一条消息由一个发件人(用户)发送给多个收件人(用户),而一个收件人(用户)可以拥有多条消息。
我创建了一个包含两列的 Messages_Recipients 表:MessageID(指的是 Messages 表的 ID 列和 Recipient(指的是 Users 表中的用户名列)。该表表示收件人和消息之间的多对多关系。

所以,我的问题是这个。新消息的 ID 将在存储到数据库后创建。但是,为了检索这个新的 MessageID,我如何保存对刚刚添加的 MessageRow 的引用?
当然,我总是可以在数据库中搜索添加的最后一行,但这可能会在多线程环境中返回不同的行?

编辑:据我了解,对于 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);
}
4

6 回答 6

116

One other option is to look at the system table sqlite_sequence. Your sqlite database will have that table automatically if you created any table with autoincrement primary key. This table is for sqlite to keep track of the autoincrement field so that it won't repeat the primary key even after you delete some rows or after some insert failed (read more about this here http://www.sqlite.org/autoinc.html).

So with this table there is the added benefit that you can find out your newly inserted item's primary key even after you inserted something else (in other tables, of course!). After making sure that your insert is successful (otherwise you will get a false number), you simply need to do:

select seq from sqlite_sequence where name="table_name"
于 2010-02-07T14:13:39.523 回答
100

使用 SQL Server,您将 SELECT 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();
}
于 2010-01-24T20:14:04.317 回答
12

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

这是他们在 doco 中声明的地方:

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

那来自sqlite.org 文档

于 2010-01-30T06:40:27.187 回答
6

来自@polyglot 解决方案的示例代码

SQLiteCommand sql_cmd;
sql_cmd.CommandText = "select seq from sqlite_sequence where name='myTable'; ";
int newId = Convert.ToInt32( sql_cmd.ExecuteScalar( ) );
于 2017-04-04T19:10:52.627 回答
5

根据Android Sqlite get last insert row id还有另一个查询:

SELECT rowid from your_table_name order by ROWID DESC limit 1
于 2018-08-21T15:10:16.023 回答
0

sqlite3_last_insert_rowid() 在多线程环境中是不安全的(并在 SQLite 上记录)但是好消息是您可以把握机会,见下文

ID 保留未在 SQLite 中实现,如果您知道数据中始终存在变体,您也可以避免使用自己的 UNIQUE 主键进行 PK。

注意:查看 RETURNING 上的子句是否不能解决您的问题 https://www.sqlite.org/lang_returning.html 由于这仅在最新版本的 SQLite 中可用并且可能有一些开销,请考虑使用它的事实如果您在对 SQLite 的请求之间有插入,那么运气不好

另请参阅是否绝对需要获取 SQlite 内部 PK,您能否设计自己的可预测 PK: https ://sqlite.org/withoutrowid.html

如果需要传统的 PK AUTOINCREMENT,是的,您获取的 id 可能属于另一个插入的风险很小。小但不可接受的风险。

一种解决方法是调用两次 sqlite3_last_insert_rowid()

#1 在我的插入之前,然后在我的插入之后 #2

如:

int IdLast = sqlite3_last_insert_rowid(m_db);   // Before (this id is already used)

const int rc = sqlite3_exec(m_db, sql,NULL, NULL, &m_zErrMsg);

int IdEnd = sqlite3_last_insert_rowid(m_db);    // After Insertion most probably the right one,

在绝大多数情况下,IdEnd==IdLast+1。这是“幸福的道路”,您可以依赖 IdEnd 作为您要查找的 ID。

否则,您必须执行额外的 SELECT 操作,您可以在其中使用基于 IdLast 到 IdEnd 的标准(如果有的话,WHERE 子句中的任何其他标准都可以添加)

使用ROWID(这是一个 SQlite 关键字)选择相关的 id 范围。

"SELECT my_pk_id FROM Symbols WHERE ROWID>%d && ROWID<=%d;",IdLast,IdEnd); 

// 注意 > in: ROWID>%zd,因为我们已经知道 IdLast 不是我们要寻找的那个。

由于在 INSERT 之后立即对 sqlite3_last_insert_rowid 进行第二次调用,因此此 SELECT 通常最多只返回 2 或 3 行。然后在 SELECT 的结果中搜索您插入的数据以找到正确的 id。

性能改进:由于对 sqlite3_last_insert_rowid() 的调用比 INSERT 快得多,(即使互斥锁可能会出错,这在统计上是正确的)我打赌 IdEnd 是正确的,并在最后展开 SELECT 结果。几乎在我们测试的所有情况下,最后一行都包含您要查找的 ID)。

性能提升:如果你有一个额外的 UNIQUE Key,那么将它添加到 WHERE 中只得到一行。

我尝试使用 3 个线程进行大量插入,它按预期工作,准备 + DB 处理占用了绝大多数 CPU 周期,然后结果是混合 ID 的奇数在 1/1000 插入的范围内(IdEnd> IdLast+1)

所以额外的 SELECT 来解决这个问题的代价是相当低的。

否则说在绝大多数插入中使用 sqlite3_last_insert_rowid() 的好处是很大的,如果使用一些小心,甚至可以安全地在 MT 中使用。

警告:事务模式下的情况稍微有点尴尬。

SQLite 也没有明确保证 ID 将是连续的和增长的(除非 AUTOINCREMENT)。(至少我没有找到有关这方面的信息,但是查看 SQLite 源代码就排除了这一点)

于 2022-01-06T19:05:52.573 回答