Wie rufe ich die letzte automatisch inkrementierte ID aus einer SQLite-Tabelle ab?


82

Ich habe eine Tabelle Nachrichten mit Spalten-ID (Primärschlüssel, automatische Inkrementierung) und Inhalt (Text).
Ich habe eine Tabelle Benutzer mit Spalten Benutzername (Primärschlüssel, Text) und Hash.
Eine Nachricht wird von einem Absender (Benutzer) an viele Empfänger (Benutzer) gesendet, und ein Empfänger (Benutzer) kann viele Nachrichten haben.
Ich habe eine Tabelle Messages_Recipients mit zwei Spalten erstellt: MessageID (bezogen auf die ID-Spalte der Messages-Tabelle und Recipient (bezogen auf die Spalte Benutzername in der Users-Tabelle). Diese Tabelle repräsentiert die Viele-zu-Viele-Beziehung zwischen Empfängern und Nachrichten.

Die Frage, die ich habe, ist folgende. Die ID einer neuen Nachricht wird erstellt, nachdem sie in der Datenbank gespeichert wurde. Aber wie kann ich einen Verweis auf die MessageRow halten, die ich gerade hinzugefügt habe, um diese neue MessageID abzurufen?
Ich kann natürlich immer in der Datenbank nach der zuletzt hinzugefügten Zeile suchen, aber das könnte möglicherweise eine andere Zeile in einer Multithread-Umgebung zurückgeben?

EDIT: Wie ich es für SQLite verstehe, können Sie die verwenden SELECT last_insert_rowid(). Aber wie rufe ich diese Anweisung von ADO.Net auf?

Mein Persistenzcode (Nachrichten und Nachrichtenempfänger sind DataTables):

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

Ich benutze SQlite (die ADO.Net-Version)
Dabblernl

Antworten:


87

Mit SQL Server würden Sie SCOPE_IDENTITY () AUSWÄHLEN, um den letzten Identitätswert für den aktuellen Prozess abzurufen.

Mit SQlite sieht es nach einer automatischen Erhöhung aus, die Sie durchführen würden

SELECT last_insert_rowid()

sofort nach dem Einsatz.

http://www.mail-archive.com/sqlite-users@sqlite.org/msg09429.html

Als Antwort auf Ihren Kommentar, um diesen Wert zu erhalten, möchten Sie SQL- oder OleDb-Code wie folgt verwenden:

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
Danke nochmal! Leider funktioniert es nicht, da die Funktion last_insert_rowid () aufgerufen werden muss, bevor die Verbindung, die zum Aktualisieren der DataTable verwendet wird, geschlossen wird. Dies kann eine Eigenart von SQLite sein ...
Dabblernl

1
Sorry, ja das stimmt wahrscheinlich. Haben Sie versucht, es nach den messagesAdapter.Update (messages) auszuführen?
MikeW

2
@Dabblernl scheint eine andere Antwort zuverlässiger zu sein, vielleicht möchten Sie die akzeptierte Antwort ändern, wenn Sie der Meinung sind, dass eine andere hilfreicher ist
MikeW

RowId ist jedoch nicht immer dasselbe wie PRIMARY KEY. AußerIf the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.
3ое Кто

110

Eine andere Möglichkeit besteht darin, sich die Systemtabelle anzusehen sqlite_sequence. Ihre SQLite-Datenbank verfügt automatisch über diese Tabelle, wenn Sie eine Tabelle mit dem Primärschlüssel für die automatische Inkrementierung erstellt haben. In dieser Tabelle verfolgt sqlite das Feld für die automatische Inkrementierung, sodass der Primärschlüssel auch nach dem Löschen einiger Zeilen oder nach einem fehlgeschlagenen Einfügen nicht wiederholt wird (mehr dazu hier http://www.sqlite.org/autoinc) .html ).

Diese Tabelle bietet also den zusätzlichen Vorteil, dass Sie den Primärschlüssel Ihres neu eingefügten Elements auch dann herausfinden können, wenn Sie etwas anderes eingefügt haben (natürlich in anderen Tabellen!). Nachdem Sie sichergestellt haben, dass Ihre Einfügung erfolgreich ist (andernfalls erhalten Sie eine falsche Nummer), müssen Sie einfach Folgendes tun:

select seq from sqlite_sequence where name="table_name"

1
Es funktioniert, wenn die Sequenz erhöht wird und nach dem Löschen der Zeilen.
Marek Bar

Gute Antwort. Seltsamerweise erhalten Sie in "DB Browser for SQLite", wenn Sie eine Zeile aus "sqlite_sequence" entfernen, keine letzte ID mehr für eine Tabelle.
CoolMind

8

Ich hatte Probleme mit der Verwendung SELECT last_insert_rowid()in einer Multithread-Umgebung. Wenn ein anderer Thread in eine andere Tabelle mit Autoinc eingefügt wird, gibt last_insert_rowid den Autoinc-Wert aus der neuen Tabelle zurück.

Hier heißt es im Dokument:

Wenn ein separater Thread ein neues INSERT für dieselbe Datenbankverbindung ausführt, während die Funktion sqlite3_last_insert_rowid () ausgeführt wird, und somit die letzte Einfügungszeilen-ID ändert, ist der von sqlite3_last_insert_rowid () zurückgegebene Wert unvorhersehbar und entspricht möglicherweise weder dem alten noch dem neuen letzten Zeilen-ID einfügen.

Das ist von sqlite.org doco


1
Ich denke, Sie könnten für jeden Thread eine separate Verbindung verwenden, aber ich habe festgestellt, dass es einen großen Leistungseinbruch gibt. In meinem Szenario kann ich nur ungefähr 15 Einfügungen pro Sekunde ausführen.
Fidel

Ich vermute, dass separate Threads das gleiche Problem darstellen würden. Leider scheint es bis heute keine thread-sichere Arbeit zu geben. Meine Vermutung ist, dass mehrere Transaktionen nicht einmal ausreichen würden. Seien Sie also vorsichtig: |
Rogerdpack

5

Beispielcode aus der @ polyglot-Lösung

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

3

Laut Android Sqlite Get Last Insert Row ID gibt es eine andere Abfrage:

SELECT rowid from your_table_name order by ROWID DESC limit 1

1
Diese Option hat eine Leistungseinbuße, da ein Sortieralgorithmus ausgeführt wird, bevor die Rowid gefunden wird
Sr. Libre

2
Vielleicht ist es weniger optimal, aber es funktioniert tatsächlich, sobald die Verbindung geschlossen und wieder geöffnet wurde, und die anderen beiden Lösungen haben es nicht für mich getan.
Francine DeGrood Taylor

Die folgende Variante vermeidet ORDER BY. Sollte schneller sein, aber ich habe das nicht getestet:SELECT MAX(rowid) FROM your_table_name
Tanius
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.