【问题标题】:Getting the Last Insert ID with SQLite.NET in C#在 C# 中使用 SQLite.NET 获取最后一个插入 ID
【发布时间】:2011-05-19 11:26:56
【问题描述】:

我有一个不那么简单的解决方案的简单问题...我目前正在将一些数据插入到这样的数据库中:

kompenzacijeDataSet.KompenzacijeRow kompenzacija = kompenzacijeDataSet.Kompenzacije.NewKompenzacijeRow();
kompenzacija.Datum = DateTime.Now;
kompenzacija.PodjetjeID = stranka.id;
kompenzacija.Znesek = Decimal.Parse(tbZnesek.Text);

kompenzacijeDataSet.Kompenzacije.Rows.Add(kompenzacija);

kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter kompTA = new kompenzacijeDataSetTableAdapters.KompenzacijeTableAdapter();
kompTA.Update(this.kompenzacijeDataSet.Kompenzacije);

this.currentKompenzacijaID = LastInsertID(kompTA.Connection);

最后一行很重要。为什么我要提供连接?好吧,有一个名为 last_insert_rowid() 的 SQLite 函数,您可以调用它并获取最后一个插入 ID。问题是它绑定到一个连接,而 .NET 似乎正在为每个数据集操作重新打开和关闭连接。我认为从表适配器获取连接会改变一些事情。但事实并非如此。

有人知道如何解决这个问题吗?也许从哪里获得持续的联系?或者更优雅的东西?

谢谢。

编辑:

这也是事务的问题,如果我想使用事务,我需要相同的连接,所以这也是一个问题......

【问题讨论】:

    标签: c# sql sqlite


    【解决方案1】:

    使用带有 SQLite 的 C# (.net 4.0),SQLiteConnection 类有一个属性 LastInsertRowId,它等于最近插入(或更新)元素的主整数键。

    如果表没有主整数键,则返回 rowID(在这种情况下,自动创建 rowID 为列)。

    请参阅https://www.sqlite.org/c3ref/last_insert_rowid.html 了解更多信息。

    对于在单个事务中包装多个命令,在事务开始之后和提交之前输入的任何命令都是一个事务的一部分。

    long rowID;
    using (SQLiteConnection con = new SQLiteConnection([datasource])
    {
        SQLiteTransaction transaction = null;
        transaction = con.BeginTransaction();
    
        ... [execute insert statement]
    
        rowID = con.LastInsertRowId;
    
        transaction.Commit()
    }
    

    【讨论】:

    • 这需要更多的支持。据我所知,这是更新应用程序的方法。
    【解决方案2】:
    select last_insert_rowid();
    

    您需要将其作为标量查询执行。

    string sql = @"select last_insert_rowid()";
    long lastId = (long)command.ExecuteScalar(sql); // Need to type-cast since `ExecuteScalar` returns an object.
    

    【讨论】:

    • rowid 应转换为 Int64:var result = (Int64)command.ExecuteScalar(sql);
    • 我发现这个答案不起作用。以下答案(由 Alex Smith 提供)确实有效。
    【解决方案3】:

    last_insert_rowid() 是解决方案的一部分。它返回一个行号,而不是实际的 ID。

    cmd = CNN.CreateCommand();
    cmd.CommandText = "SELECT last_insert_rowid()";
    object i = cmd.ExecuteScalar();
    
    cmd.CommandText = "SELECT " + ID_Name + " FROM " + TableName + " WHERE rowid=" + i.ToString();
    i = cmd.ExecuteScalar();
    

    【讨论】:

    • 这似乎不是真的。我用id INTEGER PRIMARY KEY创建了一个表,它总是返回最后插入行的生成ID,而不是行号。
    • 我同意上述评论。无需第二次查询。
    【解决方案4】:

    我正在使用 Microsoft.Data.Sqlite 包,但没有看到 LastInsertRowId 属性。但是您不必创建第二次访问数据库来获取最后一个 ID。相反,将两个 sql 语句组合成一个字符串。

    string sql = @"
        insert into MyTable values (null, @name);  
        select last_insert_rowid();";
    
    using (var cmd = conn.CreateCommand()) {
        cmd.CommandText = sql;
        cmd.Parameters.Add("@name", SqliteType.Text).Value = "John";
        int lastId = Convert.ToInt32(cmd.ExecuteScalar());
    }
    

    【讨论】:

    • Dotnet 5.0 w Microsoft.Data.Sqlite pkg 5.0.1。这是唯一对我有用的解决方案,因为 SqliteConnection 类中不存在“LastInsertRowId”属性。
    • 这个问题也非常有助于解释为什么这种方法有效:codeproject.com/Questions/830792/…
    【解决方案5】:

    SQLiteConnection 对象有一个属性,所以不需要额外的查询。 在 INSERT 之后,您只需使用用于 INSERT 命令的 SQLiteConnection 对象的 LastInsertRowId 属性。 LastInsertRowId 属性的类型是 Int64。 当然,正如您现在已经知道的那样,要使自动增量起作用,表上的主键必须设置为 AUTOINCREMENT 字段,这是另一个主题。

    【讨论】:

    • 其实这并不完全正确。 AUTOINCREMENT 只保证一个 id 不会被重用。 INTEGER PRIMARY KEY 字段将返回自动递增的 RowId,但如果之前已删除一个值,则它可能会被重用。欲了解更多信息:sqlite.org/autoinc.html
    • 谢谢,在使用库的 net core 版本时很有帮助。 SQLiteAsyncConnection.InsertAsync 方法调用将主 ID 作为 Task. 返回
    【解决方案6】:

    Microsoft 的参考资料和 SQLite 的参考资料似乎都有答案,这就是有些人让 LastInsertRowId 属性起作用而其他人没有的原因。

    我个人不使用 PK,因为它只是 rowid 列的别名。使用 rowid 的速度大约是您创建的 rowid 的两倍。如果我有一个 PK 的 TEXT 列,我仍然使用 rowid 并且只是使文本列唯一。 (仅适用于 SQLite 3。对于 v1 和 v2,您需要自己的,因为真空会改变 rowid 编号)

    也就是说,从最后插入的记录中获取信息的方法是下面的代码。由于该函数对其自身进行了左连接,因此我将其限制为 1 只是为了提高速度,即使您不这样做,主 SELECT 语句中也只会有 1 条记录。

    SELECT my_primary_key_column FROM my_table
    WHERE rowid in (SELECT last_insert_rowid() LIMIT 1);
    

    【讨论】:

      【解决方案7】:
          database = new SQLiteConnection(databasePath);
      
          public int GetLastInsertId()
          {
              return (int)SQLite3.LastInsertRowid(database.Handle);
          }
      

      【讨论】:

        【解决方案8】:
        # How about just running 2x SQL statements together using Execute Scalar?
        # Person is a object that has an Id and Name property
        
        var connString = LoadConnectionString(); // get connection string
        using (var conn = new SQLiteConnection(connString)) // connect to sqlite
        {
        
            // insert new record and get Id of inserted record
            var sql = @"INSERT INTO People (Name) VALUES (@Name);
                        SELECT Id FROM People
                        ORDER BY Id DESC";
        
            var lastId = conn.ExecuteScalar(sql, person);
        }
        

        【讨论】:

        • 实际上这不是一个糟糕的解决方案,但取决于实现,数据库的工作量要大得多。但在插入多个项目时实际上可能非常有用。
        【解决方案9】:

        在 EF Core 5 中,您可以在对象本身中获取 ID,而无需使用任何“最后插入”。

        例如:

        var r = new SomeData() { Name = "New Row", ...};
        
        dbContext.Add(r);
        
        dbContext.SaveChanges();
        
        Console.WriteLine(r.ID);
        
        

        您将获得新 ID,而无需考虑使用正确的连接或线程安全等。

        【讨论】:

          【解决方案10】:

          如果您使用的是 Microsoft.Data.Sqlite 包,它在 SqliteConnection 类中不包含 LastInsertRowId 属性,但您仍然可以使用底层的 SQLitePCL 调用 last_insert_rowid 函数图书馆。这是一个扩展方法:

          using Microsoft.Data.Sqlite;
          using SQLitePCL;
          
          public static long GetLastInsertRowId(this SqliteConnection connection)
          {
              var handle = connection.Handle ?? throw new NullReferenceException("The connection is not open.");
              return raw.sqlite3_last_insert_rowid(handle);
          }
          

          【讨论】:

            猜你喜欢
            • 2023-03-16
            • 2011-04-30
            • 2011-02-25
            • 2011-06-01
            • 1970-01-01
            • 2011-06-30
            • 2014-02-18
            • 1970-01-01
            • 2011-05-07
            相关资源
            最近更新 更多