質疑応答スタイル
何時間にもわたって問題を調査し、格闘した結果、テーブルの構造と、整合性を維持するために外部キー制限を有効にしている場合に応じて、これを達成する方法が 2 つあることを発見しました。私の状況にあるかもしれない人々に時間を節約するために、これをきれいな形式で共有したいと思います.
オプション 1: 行を削除する余裕がある
つまり、外部キーを持っていないか、持っている場合は、整合性の例外がないように SQLite エンジンが構成されています。行く方法はINSERT OR REPLACEです。ID が既に存在するプレイヤーを挿入/更新しようとすると、SQLite エンジンはその行を削除し、提供するデータを挿入します。問題は、古い ID を関連付けたままにするにはどうすればよいかということです。
データ user_name='steven' および age=32 でUPSERTしたいとしましょう。
このコードを見てください:
INSERT INTO players (id, name, age)
VALUES (
coalesce((select id from players where user_name='steven'),
(select max(id) from drawings) + 1),
32)
コツは合体です。ユーザー「steven」の ID があればそれを返し、それ以外の場合は新しい新しい ID を返します。
オプション 2: 行を削除する余裕がない
前の解決策を試してみたところ、私の場合、この ID は他のテーブルの外部キーとして機能するため、データが破壊される可能性があることに気付きました。さらに、 ON DELETE CASCADE句を使用してテーブルを作成しました。これは、データをサイレントに削除することを意味します。危険。
ということで、最初は IF 句を考えましたが、SQLite にはCASEしかありません。そして、このCASEは、存在する場合 (user_name='steven' のプレイヤーから ID を選択) の場合は 1 つのUPDATEクエリを実行し、そうでない場合はINSERTを実行するために使用することはできません (または、少なくとも私はそれを管理しませんでした) 。立ち入り禁止。
そして最後に、私は力ずくで成功しました。ロジックは、実行するUPSERTごとに、最初にINSERT OR IGNOREを実行してユーザーの行があることを確認してから、挿入しようとしたデータとまったく同じデータでUPDATEクエリを実行します。
前と同じデータ: user_name='steven' および age=32。
-- make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);
-- make sure it has the right data
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven';
そしてそれだけです!
編集
Andy がコメントしたように、最初に挿入してから更新しようとすると、予想よりも頻繁にトリガーが発生する可能性があります。私の意見では、これはデータの安全性の問題ではありませんが、不必要なイベントを発生させることはほとんど意味がありません。したがって、改善されたソリューションは次のようになります。
-- Try to update any existing row
UPDATE players SET age=32 WHERE user_name='steven';
-- Make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32);