UPDATE で既存の行を書き換える
INSERT が新しい行を追加するのに対して、UPDATE はすでに存在する行を書き換えるための文です。SQLite の update 文は構文がシンプルなので、まずは形ごと覚えてしまうのがおすすめです。
UPDATE table_name
SET column = value
WHERE condition;
実際に動かしてみる例:
SET で何を変えるかを指定し、WHERE でどの行を対象にするかを決めます。それ以外の行はそのまま残ります。
実務では WHERE を省略してはいけない
文法上 WHERE はオプションです。ただ、実務でこれを省略すると、若手エンジニアが午後を丸ごと潰すパターンに直行します。
UPDATE users SET status = 'inactive';
-- これで全てのユーザーが非アクティブになりました
フィルタを書かなければ、すべての行がマッチします。SQLiteは何も言わずにそのまま実行してしまいます。だからこそ、まず WHERE を書いてから SET を書く——この習慣をつけておくだけで、事故をぐっと減らせます。
WHERE の条件に自信がないときは、まず同じ条件で SELECT を流して確認しましょう。
同じ条件で SELECT と UPDATE を並べて書いておけば、SELECT の方がそのままドライランとして使えます。
複数カラムをまとめて更新する
SET の中はカンマ区切りで複数の代入を並べられます。SET は1つでOK、カラムはいくつでも指定できます:
1往復のクエリで1行を変更し、3つのカラムをまとめて更新できます。1つで済むところを、わざわざ3本のUPDATE文に分けて書く必要はありません。
=の右辺に式を書く
=の後ろにはリテラルだけでなく、任意の式を指定できます。もちろん、更新対象のカラム自身の現在値を参照することも可能です。
price * 1.10 は現在の価格を読み取り、1.10 倍した結果を書き戻します。SQLite は右辺を評価するときに、この文による更新が適用される 前 の行の値を使うため、複数のカラムを参照しても安全です。
UPDATE products SET price = price * 1.10, stock = stock + price;
-- ここでの右辺の 'price' は、更新後の価格ではなく、更新前の古い価格です。
UPDATE ... FROM:別テーブルから値を引っ張ってくる
SQLite 3.33以降では、UPDATE文でFROM句が使えるようになりました。テーブル間でデータを同期させたいときに、これがいちばんスッキリ書ける方法です。
サブクエリで顧客ごとの合計を計算し、外側の UPDATE がその結果を id で customers に結合し直しています。UPDATE ... FROM を使わない場合、列ごとに相関サブクエリを書く羽目になり、コードがかなりごちゃつきます。
押さえておきたいポイントをいくつか挙げておきます。
- 対象テーブルは
UPDATEの直後に書きます。FROM句には 入れません 。 - 結合条件は
WHERE句で指定します。ここではONキーワードは使いません。 FROM側で複数行にマッチする可能性がある結合は、結果が不定になります。結合キーは、ターゲット行に対して最大1件しか一致しないように設計してください。
RETURNING句で更新結果を取得する
SQLite 3.35以降では、UPDATE 文の中で更新後の行をそのまま返せるようになりました。アプリ側で更新後の値が必要なときに、わざわざ SELECT を追加で投げなくて済むので便利です。
変更が実際に適用された行を、更新後の値ごとそのまま受け取れます。ラウンドトリップを減らせるうえに、並行処理でありがちな競合状態の一部もきれいに排除できます。RETURNING については、本章のあとのページでまるごと一本取り上げます。
UPDATE OR REPLACE: 制約違反への対処
UNIQUE 制約に引っかかるような更新を実行すると、デフォルトでは文ごとエラーで中断されます。OR 句を使えば、この挙動を別のポリシーに差し替えられます。
オプションは OR ABORT(デフォルト)、OR REPLACE、OR IGNORE、OR FAIL、OR ROLLBACK の5つです。要注意なのは REPLACE で、競合した行を削除してしまうため、外部キー経由で連鎖的に影響が広がることもあります。「同じユニーク値の行がすでにあれば、古いほうは捨てる」と本当に言い切れるときだけ使ってください。
UPSERT的な処理がしたいなら、専用の INSERT ... ON CONFLICT 構文のほうが意図が伝わりやすいです。こちらは別ページで解説しています。
sqlite 一括更新はトランザクションで安全に
大量の行を書き換えるときや、複数の UPDATE 文をまとめて成功させたいときは、トランザクションで囲むのが鉄則です。途中で何か問題が起きても、実行前の状態にロールバックできます。
2 つ目の文が失敗したら(たとえば制約に引っかかったら)、ROLLBACK で 1 つ目もまとめて取り消されます。トランザクションを使わなければ、Ada から 25 引かれただけで Boris には届かない、という中途半端な送金状態が残ってしまうわけです。トランザクションについては後の章でじっくり扱いますが、ひとまず「一括更新はほぼ確実にトランザクションの中で実行する」と覚えておいてください。
よくある落とし穴
地味にハマりやすいポイントをまとめておきます。
WHEREを書き忘れる — 全行が更新されます。実行前に文を声に出して読むくらいの慎重さでちょうどいいです。WHEREで演算子を間違える —WHERE status = NULLは何にもマッチしません。IS NULLを使いましょう。詳しくは演算子のページで解説します。- 1 行返るつもりのサブクエリが複数行返ってしまう。
LIMIT 1を付けるか集約関数でまとめないと、エラーになるか、想定外の結果になります。 - UPDATE OR REPLACE と UPSERT を混同する。
OR REPLACEは衝突した行を削除します。INSERT ... ON CONFLICT DO UPDATEはその場で更新します。別物です。
次は DELETE
UPDATE は行を書き換える操作、DELETE は行を消す操作です。WHERE を丁寧に書く姿勢は同じですし、「先に SELECT で確認する」という習慣も同じように事故を防いでくれます。次のページで見ていきましょう。
よくある質問
SQLiteのUPDATE文の基本構文は?
基本形は UPDATE table_name SET column = value WHERE condition; です。SET の後ろに変更したいカラムと新しい値を書き、WHERE で対象行を絞り込みます。WHERE を書き忘れるとテーブル全体の行が更新されてしまうので要注意です。
1つのUPDATE文で複数カラムを更新するには?
SET 句の中にカンマ区切りで並べるだけです。例えば UPDATE users SET name = 'Ada', email = 'ada@x.com' WHERE id = 1; のように書きます。1文・1往復で済むので、カラムごとに SET を繰り返す必要はありません。
SQLiteで別テーブルの値を使ってUPDATEできる?
できます。SQLite 3.33で追加された UPDATE ... FROM を使えば、別テーブルやサブクエリを結合しながら更新可能です。書き方は UPDATE target SET col = source.col FROM source WHERE target.id = source.id; という感じで、テーブル間で値をコピーしたいときに一番スッキリ書けます。