91

ID(主キー、自動インクリメント)列とコンテンツ(テキスト)列を持つメッセージテーブルがあります。
ユーザー名(主キー、テキスト)とハッシュの列を持つユーザーテーブルがあります。
メッセージは1人の送信者(ユーザー)から多くの受信者(ユーザー)に送信され、受信者(ユーザー)は多くのメッセージを持つことができます。
MessageID(MessagesテーブルのID列を参照)とRecipient(Usersテーブルのusername列を参照)の2つの列を持つMessages_Recipientsテーブルを作成しました。このテーブルは、受信者とメッセージの間の多対多の関係を表します。

だから、私が持っている質問はこれです。新しいメッセージのIDは、データベースに保存された後に作成されます。しかし、この新しいMessageIDを取得するために、追加したばかりのMessageRowへの参照を保持するにはどうすればよいですか?
もちろん、データベースで最後に追加された行をいつでも検索できますが、マルチスレッド環境では別の行が返される可能性がありますか?

編集:私はSQLiteについて理解しているので、を使用できますSELECT last_insert_rowid()。しかし、ADO.Netからこのステートメントを呼び出すにはどうすればよいですか?

私の永続性コード(messagesとmessagesRecipientsは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);
}
4

6 に答える 6

116

もう1つのオプションは、システムテーブルを確認することですsqlite_sequence。自動インクリメントの主キーを使用してテーブルを作成した場合、sqliteデータベースにはそのテーブルが自動的に含まれます。このテーブルは、sqliteが自動インクリメントフィールドを追跡して、一部の行を削除した後、または一部の挿入が失敗した後でも主キーを繰り返さないようにするためのものです(詳細については、http://www.sqlite.org/autoincを参照してください)。 .html)。

したがって、このテーブルには、他の何かを挿入した後でも(もちろん、他のテーブルで)、新しく挿入されたアイテムの主キーを見つけることができるという追加の利点があります。挿入が成功したことを確認した後(そうでない場合は、誤った番号を取得します)、次のことを行う必要があります。

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

SQL Serverでは、SELECT SCOPE_IDENTITY()を使用して、現在のプロセスの最後のID値を取得します。

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値を返します。

これが彼らがドコでそれを述べているところです:

sqlite3_last_insert_rowid()関数の実行中に別のスレッドが同じデータベース接続で新しいINSERTを実行し、最後の挿入rowidを変更した場合、sqlite3_last_insert_rowid()によって返される値は予測できず、古いまたは新しい最後の値と等しくない可能性があります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

従来のPKAUTOINCREMENTが必要な場合は、取得したIDが別の挿入に属している可能性があるという小さなリスクがあります。小さいが許容できないリスク。

回避策は、sqlite3_last_insert_rowid()を2回呼び出すことです。

#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です。これは「ハッピーパス」であり、探しているIDとしてIdEndを信頼できます。

それ以外の場合は、IdLastからIdEndに基づく基準を使用できる追加のSELECTを実行する必要があります(WHERE句に追加の基準がある場合は追加することをお勧めします)

ROWID(SQliteキーワード)を使用して、関連するID範囲を選択します。

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

// IdLastが探しているものではないことはすでにわかっているので、> in:ROWID>%zdに注意してください。

sqlite3_last_insert_rowidへの2回目の呼び出しは、INSERTの直後に行われるため、このSELECTは通常、最大2行または3行のみを返します。次に、SELECTの結果で挿入したデータを検索して、適切なIDを見つけます。

パフォーマンスの向上:sqlite3_last_insert_rowid()の呼び出しはINSERTよりもはるかに高速であるため(mutexが間違っている場合でも、統計的に正しい)、IdEndが正しいものであることに賭け、最後までSELECTの結果を巻き戻します。ほぼすべての場合、最後のROWにあなたが探しているIDが含まれていることをテストしました)。

パフォーマンスの向上:追加のUNIQUEキーがある場合は、それをWHEREに追加して、1行のみを取得します。

大量の挿入を行う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 に答える