INSERT でテーブルに行を追加する
新しい行をテーブルに追加するときに使うのが INSERT 文です。sqlite insert into の書き方はとてもシンプルで、覚えやすい形になっています。
注目すべきポイントは3つあります。
INSERT INTO books- 挿入先のテーブル名。(title, author, year)- 値を入れるカラムの一覧。VALUES (...)- 実際の値。カラム一覧と同じ順序で並べます。
id はカラム一覧に含まれていないので、SQLite が自動で値を割り当ててくれます(INTEGER PRIMARY KEY なので rowid が入ります)。指定しなかったカラムにはデフォルト値が、デフォルトがなければ NULL が入ります。
カラム名は必ず明示しよう
カラム一覧を省略して、宣言順にすべてのカラムへ値を渡すこと自体は できます。
-- 動作はするが、壊れやすい:
INSERT INTO books VALUES (NULL, 'Dune', 'Frank Herbert', 1965);
やめておきましょう。books に誰かがカラムを 1 つ追加した瞬間、こういう書き方の INSERT 文はエラーになるか、最悪の場合は別のカラムに値が入ってしまいます。カラム名はきちんと明示しましょう:
カラム名を明示的に書いておくと、それ自体がドキュメントの役割を果たします。テーブル定義をいちいち見に行かなくても、何をしているSQLなのか一目で分かるようになります。
SQLiteで複数行をまとめてINSERTする
SQLiteでは、値のタプルをカンマで並べることで、1つのINSERT文で複数行を一気に挿入できます。
これは別々の INSERT 文を3つ書くよりスッキリしますし、SQLite はこれ全体をひとつの文として扱います。ただし、大量データを投入するときに本当に効いてくるのは、INSERT をトランザクションで囲むことです。次はその話をします。
sqlite 一括 insert はトランザクションでまとめる
デフォルトでは、INSERT 文はそれぞれが独立したトランザクションになります。SQLite は1件ごとに fsync を実行するので、素朴なループが遅くなる原因はじつは INSERT 自体ではなく、この同期処理なんです。
まとめて流しましょう。
5回の fsync が1回で済みます。数千行規模になると、その差は2〜3桁になることもあります。途中で何か失敗しても ROLLBACK でバッチ全体を巻き戻せるので安心です。
これがsqliteの一括 insertの定番パターンです。Python、Node、Rust など、どの言語からSQLiteを呼び出す場合でも同じで、ループ全体を BEGIN / COMMIT で囲んでおきましょう。
INSERT ... SELECT:別テーブルからのコピー
リテラルの値ではなく、クエリの結果からテーブルにデータを流し込むこともできます。
SELECT の各列は名前ではなく並び順で INSERT の列リストに対応付けられます。名前は一致しなくてOKで、順序さえ合っていれば問題ありません。これは行のアーカイブ、集計用テーブルの作成、マイグレーション時に一部のデータをコピーする、といった場面で定番のやり方です。
DEFAULT VALUES と省略した列の扱い
列に DEFAULT 句が指定されていれば、その列を列リストから外しておくだけでSQLiteが既定値を補ってくれます。
created_at には値を渡していないので、自動的に現在のタイムスタンプが入ります。すべての列をデフォルト値で埋めた行を作りたいときは(プレースホルダ的な行を入れたい場面で便利です)、DEFAULT VALUES 構文を使いましょう:
新しい行が2つ追加され、どちらも value = 0 で id は自動採番されます。
INSERT OR IGNORE:重複をスキップする
UNIQUE 制約や PRIMARY KEY 制約に違反する行を挿入しようとすると、デフォルトではエラーが発生して文全体が中断されます。
エラー: UNIQUE制約に違反しました: users.email
INSERT OR IGNORE を使えば、競合した行は「黙ってスキップ」する動きに切り替えられます:
残った行は3件。重複した行はエラーを出さずに静かに弾かれます。シンプルなシードデータに対して「存在しなければ挿入する」を表現する、SQLite らしいイディオムです。事前に SELECT でチェックしたり、例外処理を書いたりする必要はありません。
INSERT OR REPLACE: 重複行を上書きする
INSERT OR REPLACE は、競合した行を削除してから新しい行をその位置に挿入します。
ひとつだけ注意点があります。REPLACE は UPDATE ではなく DELETE + INSERT です。削除される行に対して ON DELETE CASCADE 付きの外部キーが張られていれば、子レコードも一緒に消えます。さらに、新しい INSERT で指定しなかったカラムは元の値を保持せず、デフォルト値にリセットされます。
「あればUPDATE、なければINSERT」をやりたいケースのほとんどは、本来の upsert である ON CONFLICT ... DO UPDATE を使うのが正解です。これについては別ページで詳しく解説します。
ここまでのおさらい
INSERT INTO table (cols) VALUES (...)が基本形。カラム名は必ず明示する。- sqlite で複数行 insert するときは、
VALUESのあとにタプルをカンマ区切りで並べる。 - 本格的な一括 insert では、
BEGIN/COMMITでまとめてトランザクションにする。 INSERT INTO ... SELECT ...でクエリ結果をそのまま流し込める。DEFAULT VALUESを使えばデフォルト値だけで1行作れる。省略したカラムも同じくデフォルトが入る。INSERT OR IGNOREは競合した行をスキップ、INSERT OR REPLACEは削除→挿入で上書きする。
次は UPDATE
行を追加する話はこれで一区切り。残りのもう半分は、すでに存在する行を書き換える話です。カウンタを増やしたり、タイプミスを直したり、注文を発送済みにしたり--そこで登場するのが UPDATE です。特に WHERE 句まわりに、押さえておきたいクセがいくつかあります。続きは次のページで。
よくある質問
SQLiteで1行だけ挿入するには?
INSERT INTO テーブル名 (col1, col2) VALUES (val1, val2); の形で書きます。カラム名のリストは省略できますが、明示しておくのがおすすめ。あとからカラムが追加されてもこの文はそのまま動き続けます。省略する場合は、テーブル定義の順番どおりに全カラム分の値を渡す必要があります。
SQLiteで複数行をまとめてINSERTするには?
VALUES のあとにカッコで囲んだタプルをカンマ区切りで並べればOKです。例: INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);。ただし、数千行レベルの本格的な一括ロードでは BEGIN と COMMIT でトランザクションにまとめるのがポイント。実は速くなる理由は複数行構文ではなく、こちらのトランザクション化のほうです。
INSERT OR IGNORE はどんな動きをする?
INSERT OR IGNORE は、UNIQUE・PRIMARY KEY・NOT NULL 制約に引っかかる行をエラーにせず黙ってスキップします。衝突した行だけが捨てられて、残りの行はそのまま挿入されます。「すでに存在しなければ追加」という動きを、わざわざ存在チェックを書かずに実現したいときに便利です。
INSERT時に『UNIQUE constraint failed』が出るのはなぜ?
UNIQUE または PRIMARY KEY カラムに、すでに同じ値の行が存在することをSQLiteが検知したからです。本当に値が重複しているか、シードスクリプトを再実行しているケースが大半。重複を黙って飛ばしたいなら INSERT OR IGNORE、上書きしたいなら INSERT OR REPLACE、もっと細かく制御したいなら ON CONFLICT ... DO UPDATE(いわゆるupsert)を使い分けましょう。