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)を使い分けましょう。