CSVインポートはSQLではなくCLIの機能
実は SQLite の SQL 構文には IMPORT 文が存在しません。CSV の取り込みは sqlite3 コマンドラインシェルの機能で、.import というドットコマンドで実行します。MySQL の LOAD DATA INFILE や PostgreSQL の COPY から来た人にとっては、ここが大きな発想の転換ポイントです。あちらはサーバー側で動く処理ですが、.import はクライアントツールがファイルを読み込み、裏側で INSERT 文を発行してくれる仕組みになっています。
というわけで、このページの内容はすべて sqlite3 シェルに入った状態を前提にしています。
sqlite3 mydata.db
アプリケーションのコード(Python、Node、Go など)から取り込みたい場合は、各言語側で CSV を読み込み、パラメータ化した INSERT 文を実行する形になります。そのやり方はアプリ連携の章で取り上げます。ここでは CLI に絞って解説します。
.import コマンドの基本
最短ルートは、SQLite に「これは CSV だ」と伝えてから、.import にファイルとテーブル名を渡すだけです。
.mode csv
.import people.csv people
people テーブルがすでに存在するかどうかで、挙動が2パターンに分かれます。
- テーブルが存在しない場合 — SQLite が自動で作成し、CSV の1行目をカラム名として使います。すべてのカラムは
TEXTアフィニティになります。 - テーブルが存在する場合 — SQLite はファイルの全行をデータとして INSERT します。ヘッダー行があれば、それも1件のデータとして取り込まれてしまいます。
最初に sqlite3 import csv を試したときに多くの人がハマるのが、この2つ目のケースです。CSV にヘッダーがあって、なおかつテーブルがすでに存在する場合は、明示的にヘッダーをスキップする必要があります。
既存テーブルに CSV をインポートしてヘッダーをスキップする
.import に最初の N 行を無視させるには、--skip 1 を指定します。
CREATE TABLE people (
name TEXT,
age INTEGER,
city TEXT
);
.import --csv --skip 1 people.csv people
--csv は .mode csv をこの 1 コマンドだけに効かせるショートハンドなので、わざわざモードを切り替える必要はありません。--skip 1 で先頭のヘッダー行を読み飛ばし、残りの行はカラム順のまま people テーブルに INSERT されます。
インポートできたか、ざっと確認してみましょう。
SELECT count(*) FROM people;
SELECT * FROM people LIMIT 5;
ファイルの列順は、テーブルの列順とそのまま一致している必要があります。ヘッダー名でマッピングしてくれるわけではなく、.import は単純に N 番目のフィールドを N 番目のカラムに流し込むだけです。
SQLite に CSV からテーブルを自動生成させる
ちょっと触って試したいだけなら、CREATE TABLE を書かずに .import にヘッダー行からテーブルを作らせるのが一番手軽です。
.mode csv
.import sales.csv sales
.schema sales
.schema sales を実行すると、こんな感じの出力が返ってきます。
CREATE TABLE sales(
"order_id" TEXT,
"amount" TEXT,
"ordered_at" TEXT
);
カラムがすべて TEXT になっている点に注目してください。これは意図的な挙動で、.import は型を推測しようとしません。amount を本物の数値として、ordered_at をきちんとしたタイムスタンプとして扱いたい場合は、先に適切な型でテーブルを自分で作成し、それから --skip 1 付きでインポートしてください。SQLite の型親和性(type affinity)が、INSERT 時に数値文字列を整数や実数へ自動的に変換してくれます。
カスタム区切り文字: TSV、パイプ、セミコロン
.mode csv はカンマ区切り用です。タブ区切りファイル(TSV)を読み込みたいときは、モードを切り替えます:
.mode tabs
.import data.tsv events
別の区切り文字を使いたい場合は、モードを指定したあとに .separator を実行します。
.mode csv
.separator "|"
.import pipe_data.txt events
知っておきたいポイントとして、.mode csv は RFC 4180 のクオート規則に従います。フィールド内にカンマや改行が含まれていても、" で正しく囲まれていれば問題なく読み込めます。一方の .mode tabs は、単純に区切り文字で分割するだけのモードで、クオート処理はありません。クオートされたフィールドの中に区切り文字が混ざっているようなファイルなら、.mode csv のまま区切り文字だけを変更するのがおすすめです。
実際の流れを見てみよう
たとえば orders.csv が次のような内容だったとしましょう。
order_id,customer,amount,ordered_at
1001,Ada,49.99,2026-01-12
1002,Boris,12.50,2026-01-13
1003,"Chen, Wei",199.00,2026-01-14
行3の引用符付きフィールドの中にカンマが含まれている点に注目してください。実際のセッションは以下のとおりです。
実際のシェルでは、INSERT のブロックをまるごと .import --csv --skip 1 orders.csv orders の一行に置き換えられます。CSVモードはクォートを正しく解釈してくれるので、"Chen, Wei" のフィールドもそのままの形で取り込まれます。amount は数値として、order_id は整数として格納されるのは、カラムの型定義のおかげです。
トランザクションでまとめてインポートする
.import は1行ごとに INSERT を発行します。数千行程度なら問題ありませんが、100万行ともなると話は別です。1行ごとにコミットされるため、トランザクションで全体を囲ってあげないと、目を覆いたくなるほど遅くなります。
BEGIN;
.import --csv --skip 1 big_file.csv events
COMMIT;
この一手間だけで、数分かかっていたインポートが数秒で終わることもあります。途中で失敗しても ROLLBACK で部分的な読み込みを取り消せるので、リトライ時にも便利です。
さらに高速化したいなら、インポート前にインデックスを削除しておき、終わってから作り直すといいでしょう。1行ごとのインデックス更新は、積み重なるとかなりの負荷になります。
よくあるエラーと対処法
Error: expected N columns but found M — 行のカラム数がテーブルと一致していません。原因はだいたい次のどれかです。
- クォートされていないフィールドにカンマが紛れ込んでいる。CSV を正しくクォートして再エクスポートするか、
.mode tabsではなく.mode csv(RFC 4180 準拠)に切り替えましょう。 - ファイル末尾に空行が残っている。ファイルを編集するか、
--skipをうまく使って回避します。 - テーブルのカラム数が CSV より多い。CSV 側に不足分を追加するか、形が合うステージング用テーブルにいったん入れてから本番テーブルへコピーします。
ヘッダー行がデータとして入ってしまう — 既存テーブルへのインポート時に --skip 1 を付け忘れたパターンです。DELETE FROM t WHERE rowid = 1 でその行を消し、フラグ付きで再実行してください。
数値が文字列として保存される — .import にテーブルを自動作成させると、すべてのカラムが TEXT になります。テーブルを削除し、INTEGER や REAL を明示してから再インポートしましょう。
Error: no such file — パスはデータベースファイルからの相対ではなく、sqlite3 を起動したディレクトリからの相対になります。絶対パスを使うか、シェルを開く前に該当ディレクトリへ cd しておきましょう。
CLI はエラー時に行番号を出してくれるので、大きなファイルから問題の行を探すにはこれが一番早道です。
ここまでのまとめ
.importは SQL ではなく CLI のドットコマンド。sqlite3シェルの中で実行します。- クォート処理には
--csv、ヘッダーをスキップしたいときは--skip 1を使います。 - テーブルが存在しない場合、
.importはヘッダーから自動でテーブルを作りますが、カラムはすべてTEXTになります。型を効かせたいなら、自分でCREATE TABLEしておきましょう。 - 大量のデータをインポートするときは
BEGIN/COMMITで囲み、1行ごとのトランザクションを避けます。 - ファイルのカラム順は、テーブルのカラム順と一致している必要があります。
次回:データを書き出す
インポートはあくまで半分の話。同じシェルを使えば、クエリ結果やテーブル全体を CSV、JSON、SQL として出力できます。バックアップやデータパイプライン、他ツールへの受け渡しにも欠かせない機能です。次回のデータエクスポート編で詳しく見ていきます。
よくある質問
SQLiteにCSVファイルをインポートするには?
sqlite3 CLIでデータベースを開き、.mode csvでCSVモードに切り替えてから.import data.csv table_nameを実行します。テーブルが存在しない場合は、CSVの1行目をカラム名としてSQLiteが自動でテーブルを作成してくれます。逆に既存のテーブルに取り込む場合は1行目もデータ行として挿入されてしまうので、.import --skip 1でヘッダー行をスキップするのが一般的です。
ヘッダー付きCSVを既存のSQLiteテーブルにインポートするには?
.import --csv --skip 1 data.csv table_nameを使います。--skip 1を付けることで1行目を無視できるので、ヘッダー行がそのままデータとして登録されることがありません。これを忘れると、カラム名そのものが入った変な行が1件できてしまうので注意してください。
「expected N columns but found M」エラーでインポートが失敗するのはなぜ?
テーブルのカラム数とCSV側の列数が合っていないのが原因です。多くの場合、フィールド内のカンマ、エスケープされていないダブルクォート、末尾の空行などが犯人です。.mode tabsではなく.mode csv(または--csv)を使えばRFC 4180準拠でクォートを処理してくれるので、まずはそちらを試してください。CLIは問題のある行番号も表示してくれるので、テキストエディタで該当行を開いて区切り文字の混入をチェックするのが一番早い解決策です。