¿Cómo recuperar la última ID autoincrementada de una tabla SQLite?

82

Tengo una tabla de Mensajes con ID de columnas (clave principal, autoincremento) y Contenido (texto).
Tengo una tabla de Usuarios con columnas de nombre de usuario (clave principal, texto) y Hash.
Un remitente (usuario) envía un mensaje a muchos destinatarios (usuario) y un destinatario (usuario) puede tener muchos mensajes.
Creé una tabla Messages_Recipients con dos columnas: MessageID (refiriéndose a la columna ID de la tabla Messages y Recipient (refiriéndose a la columna de nombre de usuario en la tabla Users). Esta tabla representa la relación de muchos a muchos entre destinatarios y mensajes.

Entonces, la pregunta que tengo es esta. El ID de un mensaje nuevo se creará después de que se haya almacenado en la base de datos. Pero, ¿cómo puedo tener una referencia al MessageRow que acabo de agregar para recuperar este nuevo MessageID?
Siempre puedo buscar en la base de datos la última fila agregada, por supuesto, pero ¿eso podría devolver una fila diferente en un entorno multiproceso?

EDITAR: Según tengo entendido para SQLite, puede usar SELECT last_insert_rowid(). Pero, ¿cómo llamo a esta declaración de ADO.Net?

Mi código de persistencia (los mensajes y los destinatarios de los mensajes son tablas de datos):

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
fuente
Yo uso SQlite (la versión ADO.Net)
Dabblernl

Respuestas:

88

Con SQL Server, SELECCIONE SCOPE_IDENTITY () para obtener el último valor de identidad para el proceso actual.

Con SQlite, parece que para un autoincremento harías

SELECT last_insert_rowid()

inmediatamente después de su inserción.

http://www.mail-archive.com/[email protected]/msg09429.html

En respuesta a su comentario para obtener este valor, querrá usar código SQL u OleDb como:

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
fuente
2
¡Gracias de nuevo! Desafortunadamente, no funciona ya que es necesario llamar a la función last_insert_rowid () antes de que se cierre la conexión que se usa para actualizar DataTable. Sin embargo, esto puede ser una peculiaridad de SQLite ...
Dabblernl
1
Lo siento, sí, probablemente sea cierto. ¿Intentó ejecutarlo después de messagesAdapter.Update (messages);
MikeW
2
@Dabblernl parece que otra respuesta es más confiable, es posible que desee cambiar la respuesta aceptada si cree que otra es más útil
MikeW
Pero RowId no siempre es lo mismo que PRIMARY KEY. ExceptoIf the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.
Кое Кто
110

Otra opción es mirar la tabla del sistema sqlite_sequence. Su base de datos sqlite tendrá esa tabla automáticamente si creó cualquier tabla con clave primaria de autoincremento. Esta tabla es para que sqlite realice un seguimiento del campo de autoincremento para que no repita la clave principal incluso después de eliminar algunas filas o después de que fallara alguna inserción (lea más sobre esto aquí http://www.sqlite.org/autoinc .html ).

Entonces, con esta tabla existe el beneficio adicional de que puede encontrar la clave principal de su elemento recién insertado incluso después de haber insertado algo más (¡en otras tablas, por supuesto!). Después de asegurarse de que su inserción sea correcta (de lo contrario, obtendrá un número falso), simplemente debe hacer:

select seq from sqlite_sequence where name="table_name"
polígloto
fuente
1
Funciona cuando se incrementa la secuencia y después de la eliminación de filas.
Marek Bar
Buena respuesta. Es extraño que en "DB Browser for SQLite", si elimina una fila de "sqlite_sequence", ya no obtendrá una última identificación para alguna tabla.
CoolMind
8

He tenido problemas con el uso SELECT last_insert_rowid()en un entorno multiproceso. Si otro hilo se inserta en otra tabla que tiene un autoinc, last_insert_rowid devolverá el valor autoinc de la nueva tabla.

Aquí es donde afirman que en el doco:

Si un subproceso separado realiza un nuevo INSERT en la misma conexión de base de datos mientras se está ejecutando la función sqlite3_last_insert_rowid () y, por lo tanto, cambia el último identificador de fila insertado, entonces el valor devuelto por sqlite3_last_insert_rowid () es impredecible y puede que no sea igual al anterior o al nuevo último insertar rowid.

Eso es de sqlite.org doco

Fidel
fuente
1
Supongo que podría usar una conexión separada para cada hilo, pero he notado que tiene un gran impacto en el rendimiento. Solo puedo hacer unas 15 inserciones por segundo en mi escenario.
Fidel
Supongo que los hilos separados serían el mismo problema. Desafortunadamente, no parece haber una solución alternativa segura para subprocesos hasta la fecha, mi corazonada es que múltiples transacciones ni siquiera serían suficientes ... así que tenga cuidado: |
rogerdpack
5

Código de muestra de la solución @polyglot

SQLiteCommand sql_cmd;
sql_cmd.CommandText = "select seq from sqlite_sequence where name='myTable'; ";
int newId = Convert.ToInt32( sql_cmd.ExecuteScalar( ) );
O'Neil
fuente
4

Según Android Sqlite, obtenga la última identificación de fila de inserción, hay otra consulta:

SELECT rowid from your_table_name order by ROWID DESC limit 1
CoolMind
fuente
1
Esta opción tiene una penalización de rendimiento debido a la ejecución de un algoritmo de clasificación antes de encontrar el rowid
Sr. Libre
2
Tal vez sea menos óptimo, pero en realidad funciona una vez que la conexión se ha cerrado y reabierto, y las otras dos soluciones no me funcionaron.
Francine DeGrood Taylor
La siguiente variante evita ORDER BY. Debería ser más rápido, pero no SELECT MAX(rowid) FROM your_table_name
probé