Menu

SQLite CREATE TABLEの使い方と制約・実例まとめ

SQLiteでテーブルを作成する方法を解説。カラム定義、各種制約、IF NOT EXISTS、一時テーブル、CREATE TABLE AS SELECTまで実例付きで紹介します。

このページのコードはエディタで実行できます — 編集してすぐに結果を確認できます。

CREATE TABLE でスキーマを定義する

SQLite に保存される構造化データは、すべてテーブルに格納されます。そして、どのテーブルも必ず CREATE TABLE 文から始まります。テーブル名を決めて、カラムを並べ、必要に応じて制約を付ける——これだけです。実行すると SQLite はスキーマをデータベースファイルに書き込み、すぐにそのテーブルが使えるようになります。

最小限の例を見てみましょう。

3つのカラム、1つの主キー、そして1つの NOT NULL 制約。id は INTEGER PRIMARY KEY なので SQLite が自動で値を割り当ててくれます。2行目の email は何も指定していないので NULL のままです。テーブル名、カラム、制約──このページで説明する内容はすべて、この基本形のバリエーションにすぎません。

CREATE TABLE 構文を分解して理解する

カラム定義の書き方は カラム名 型 制約 制約 ... という形です。クラシックな SQLite では型の指定は省略できますが(詳しくは型親和性のページを参照)、常に明示しておくのがおすすめです。コードを読む人もツールも、型情報を手がかりにしているからです。

いくつかポイントを押さえておきましょう。

  • 制約はスペース区切りでつなげて書きます。sku に付けた NOT NULL UNIQUE は、両方のルールが効くという意味です。
  • in_stockDEFAULT 1 を指定しておけば、INSERT のときにそのカラムを省略できます。
  • SQLite は真偽値を INTEGER で扱います。ネイティブな BOOLEAN 型はありません。0 が false、1 が true です。
  • 最後のカラムの後ろにカンマを残すと構文エラーになります。SQL は JavaScript よりも厳しいので注意してください。

sqlite create table if not exists で再実行時のエラーを防ぐ

すでに同じ名前のテーブルが存在するデータベースに対して CREATE TABLE を実行すると、SQLite はエラーを投げます。

Error: table users already exists

一度きりならいいのですが、何百回もとなるとさすがにうんざりします。そこで IF NOT EXISTS を付けておけば、テーブルがすでに存在する場合は何もしない(no-op)扱いになります。

2 回目の CREATE TABLE は何も起きません。エラーも出ないし、スキーマも変わりません。起動時のコードやマイグレーションスクリプトなど、同じ SQL が複数回実行される可能性がある場所では、この書き方が正解です。

ただし注意点があります。IF NOT EXISTS がチェックしているのは 名前だけ です。同じ名前のテーブルが存在していて中身のカラムが違っていても、SQLite は何もしません。スキーマを「直して」くれたり「アップグレード」してくれたりはしないのです。そのための仕組みがマイグレーションです。

制約 (Constraints): スキーマと一緒に持ち運べるルール

制約を使えば、バリデーションをデータベース側に押し込めます。実務でよく使うのは次の 4 つです。

  • PRIMARY KEY — 行を一意に識別するための制約。詳しくはプライマリキーのドキュメントで解説します。
  • NOT NULL — その列には必ず値を入れる必要があります。
  • DEFAULT 値INSERT でその列を省略したときに使われる既定値。リテラルだけでなく datetime('now') のような式も指定できます。
  • CHECK (式) — すべての行で式が真になる必要があります。
  • UNIQUE (col, col) — テーブルレベルの制約で、列の組み合わせがユニークであることを保証します。

制約は INSERTUPDATE のたびにチェックされ、違反した行は弾かれてステートメント自体が失敗します。アプリ側に不正データが広がってから気づくより、データベースの段階で止めてしまうほうが圧倒的に低コストです。

sqlite の外部キー制約

外部キーは「この列は別のテーブルの行を指している」と宣言するための仕組みです。これによってデータの整合性が保たれ、存在しないユーザーを参照することができなくなります。さらにオプションを指定すれば、ユーザーを削除したときに紐づく注文も連鎖的に削除する、といった動きも実現できます。

SQLiteで覚えておきたい落とし穴があります。外部キーの強制は、デフォルトでオフになっているのです。制約をきちんとチェックさせたい場合は、接続ごとに PRAGMA foreign_keys = ON を実行する必要があります。多くのアプリケーションドライバはこれを自動でやってくれるか、設定項目として用意していますが、もし自分の使っているドライバにそれがなければ、接続直後に手動でこのプラグマを実行してください。

ここで指定している ON DELETE CASCADE は、「ユーザーを削除すると、そのユーザーの投稿も自動的に削除される」という意味です。他にも SET NULLRESTRICT、そしてデフォルトの NO ACTION(子レコードが存在する場合は削除を拒否)といったオプションがあります。

CREATE TABLE AS SELECT でテーブルをコピーする

クエリの結果をそのまま新しいテーブルとして保存したい場面があります。スナップショットを取りたいとき、バックアップを残したいとき、あるいは分析中に一時的な作業用テーブルが欲しいときなどです。そんなときに使えるのが CREATE TABLE ... AS SELECT です。

新しいテーブルには、カラム名・型(できる範囲で)・データがコピーされます。一方で コピーされないもの にも注意が必要です。プライマリキー、NOT NULL 制約、インデックス、外部キーはいずれも引き継がれません。あくまでフラットなスナップショットなので、その場の作業用の出発点として使うのが無難で、本番のスキーマを丸ごと複製する方法としては向きません。

データはいらず、構造だけ欲しい場合は WHERE 0 を付けます。

同じカラム構成の空テーブルが手に入るので、あとからデータを入れるアーカイブ用テーブルを作りたいときに便利です。

一時テーブル(TEMP テーブル)

TEMP テーブルは、現在のデータベース接続が生きている間だけ存在します。接続を閉じれば自動的に消えるので、後片付けもいらず、スキーマも残りません。

良い使いどころとしては、複数ステップにまたがるクエリの行を一時的に置いておく、CTEで書くには複雑すぎる中間結果を保持する、長時間動かしているセッション内で接続ごとのデータを分離する、といったケースが挙げられます。なお、CREATE TEMP TABLECREATE TEMPORARY TABLE はまったく同じ意味です。

AS SELECT と組み合わせることもできます。CREATE TEMP TABLE snapshot AS SELECT ... は、分析の途中で結果セットをスナップショットとして固定したいときによく使うパターンです。

名前のクォート

カラム名やテーブル名は、基本的にそのまま(裸の識別子として)書きます。ただし予約語を使いたい場合や、スペースを含む名前を使いたい場合は、ダブルクォート(SQL標準)かバッククォート(MySQL由来ですが、SQLiteでも受け付けます)で囲みます。

動きはしますが、テーブルを参照するたびに地味なストレスになります。ordersselectionuser_id のようにシンプルな名前にして、クォートは省くのがおすすめです。

実践的なサンプル

ここまでの内容をまとめて、タスク管理アプリ用の小さなスキーマを書いてみましょう。起動のたびに実行できるよう、IF NOT EXISTS を付けています。

これでもう本番に出せるスキーマです。べき等にテーブル作成ができて、外部キーもちゃんと効いていて、CHECKdone の値を縛り、デフォルト値も気が利いていて、タイムスタンプは勝手に入ります。

次は「データ型」の話

CREATE TABLE では INTEGERTEXTREAL といった型を書けますが、SQLite はそれをどう格納するかについて、かなりおおらかなことで知られています。次のページでは、SQLite が実際に使っている5つのストレージクラスと、書いた型と実際に格納される型が一致しないことがあるのはなぜか、を解説していきます。

よくある質問

SQLiteでテーブルを作成するには?

基本は CREATE TABLE テーブル名 (列1 型, 列2 型, ...) の形で書きます。各列には名前と型(省略可)を指定し、必要に応じて PRIMARY KEYNOT NULLDEFAULT といった制約を付けられます。文を実行するとその場でテーブルが作られ、データベースファイルに保存されます。

CREATE TABLEの IF NOT EXISTS は何をしてくれる?

CREATE TABLE IF NOT EXISTS テーブル名 (...) は、同じ名前のテーブルがまだ存在しないときだけ作成します。これを付けずに既存DBに対してスクリプトを再実行すると table already exists エラーになります。マイグレーションスクリプトやアプリ起動時の初期化処理では定番の書き方です。

SELECTの結果からテーブルを作れる?

はい。CREATE TABLE 新テーブル名 AS SELECT ... で、クエリの結果セットからそのまま新しいテーブルを作れます。ただし列名とデータはコピーされますが、元テーブルの制約・主キー・インデックスは引き継がれません。スナップショットや作業用テーブルには便利ですが、本番用のスキーマ定義の代わりには使わないほうが無難です。

一時テーブル(TEMP)と通常のテーブルは何が違う?

CREATE TEMP TABLE(または CREATE TEMPORARY TABLE)で作るテーブルは、現在のDB接続でしか使えず、接続を閉じると自動的に消えます。一方、通常のテーブルはデータベースファイルに永続化されます。一時テーブルは、本番のスキーマを汚さずに中間結果を一時的に置いておきたいときに重宝します。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める