SQLiteは、サーバなしで使えるファイルベースのデータベースである。軽量でちゃちゃっと使えるくせに、SQL仕様をほぼ網羅している。お勉強に最適だ。
macOSではプリインストールされており、下準備なしで使える。もしインストールされていなければ、SQLiteの公式サイトからダウンロードできる。
データベースの作成
SQLiteプロジェクトでは、sqlite3というコマンドラインツールを提供している。起動には以下のコマンドを使う。
データベースファイルへのパスを指定すると、ファイルが存在する場合はそのデータベースが開かれる。存在しない場合は、新たに作成される。SQLiteには、CEATE DATABASEのようなコマンドは存在せず、この方法でしかデータベースを作れない。その代わり、複数のデータベースに跨った操作を可能にする’アタッチ’という概念がある。
ちなみに、dbファイルを指定せずにsqlite3を起動すると、一時的なインメモリデータベースが自動で作成される。これはsqlite3が停止すると削除される。途中でデータベースを開きたいときは、.open /path/to/データベース.dbコマンドを使う。
ドットコマンド
sqlite3のコマンドは、ドットコマンドとPRAGMAコマンドに大別される。前者はコマンドラインツール自身に対する指示、後者はデータベースシステムに対する指示を行う。……らしいが、どこに境界があるのか正直よくわからない。
以下は基本的なドットコマンドの一例である。
バージョン情報の表示
sqliteのソースコード、圧縮ライブラリ、Cコンパイラのバージョンを表示。
カラムのヘッダを表示
クエリ結果にカラムのヘッダー表示する。デフォルトはOFF。
データベースの一覧を表示
接続 or アタッチされているデータベースとパスの一覧を表示する。
テーブルの一覧を表示
データベース内のテーブル名の一覧を表示。
スキーマの表示
指定したテーブルのスキーマを表示。テーブル名を省略すると、データベース全体のスキーマが表示される。
NULL値の表示を設定
NULLを表示する際の文字列を指定。
テーブルの出力形式を変更
テーブルの出力形式を変更する。選択肢はascii,box,csv,column,html,insert,json,line,list,markdown,quote,table,tabs,tcl。デフォルトはlist。
出力先の変更
出力先をターミナル画面から任意のファイルに変更する。ターミナル画面に戻したい時はファイルパスを省略する。
データベースの中身をダンプ
データベースの内容をSQL文として表示。バックアップに使える。デフォルトの出力先はターミナル画面。他のコマンドと同じように、.outputコマンドで任意のファイルに出力できる。
特定のテーブルをダンプ
テーブル名を渡すと、特定のテーブルのみをダンプできる。
任意のファイルに出力したい場合は、例えば以下のようにする。
ダンプしたファイルの読み込み
任意のファイルを読み込み、SQLコマンドであれば実行する。そうでなければ、パースエラーを出力して終了する。
データベースのバックアップ
データベースのバックアップには、.backupコマンドを使う。dumpはSQL文が書かれたテキストファイルを出力するのに対し、こちらはデータベースのコピー(バイナリファイル)を出力する。
そのため.readコマンドでは読み込めず、sqlite3でデータベースとして開く。
プロンプトの終了
sqlite3を終了する。
ドットコマンドのヘルプを表示
ドットコマンドの一覧を概要付きで表示する。オプションなどの詳細は、公式ドキュメントのここに書かれている。
PRAGMAコマンド
PRAGMAコマンドは、SQLiteライブラリの動作を変更したり、SQLiteライブラリに内部(テーブル以外の)データを問い合わせたりするために使用される。
らしいが、ざっとドキュメントを眺めても使うシチュエーションに馴染みがなく、よくわからなかった。経験を積みながら徐々にまとめていきたい。
一覧はここ。
以下は、PRAGMAコマンドの概要である。
- 最大1つの引数を受け取る。
- 引数は括弧で囲む(
PRAGMA xx('yy'))。 - あるいは、等号でプラグマ名と区切る(
PRAGMA xx = 'yy')。 1、yes、true、onは、大文字小文字問わず、いずれも真として解釈される。0、no、false、offは、大文字小文字問わず、いずれも偽として解釈される。- 文末に
;をつける。
任意のテーブルの列の一覧を表示
列名だけの表示はできないため、テーブル情報を表示して確認する。
以下のようにも書ける。
外部キーが有効・無効を確認する
SQLiteでは、外部キーがデフォルトで無効化されている。はずだ。有効・無効の確認には、foreign_keysというコマンドを引数なしで使う。
外部キーの有効・無効を切り替える
外部キーを有効にする場合は真の値(1、yes、true、on)のいずれかを指定する。
無効にする場合は偽の値(0、no、false、off)のいずれかを指定する。
コメント
SQLiteのコメントは、以下のいずれかで記述する。
型アフィニティ
SQLiteは、その他のSQLデータベースエンジンと違い、カラム単位の厳密な静的型付けを使用していない。
ただ、完全にノールールではなく、型アフィニティという仕組みで緩めに動的な型付けを行う。’Affinity’という単語は、親和性や相性などの意味を持つ。一見ニュアンスが謎だ。物質同士の化学反応のしやすさを化学的親和性(Chemical affinity)と表現する。これと同じニュアンスである。多分。
例えば、あるカラムの型アフィニティにINTEGERが指定されている場合、’0’は数値の0として保存され、’零’はそのまま文字列として保存される。
指定できる型
型アフィニティには、どんな文字列でも指定できる(ドキュメントには、これに対する丁寧な説明も用意されている)。
そのためSQLiteでは、他のSQLデータベースエンジン用に書かれたコマンド柔軟に受け入れることができる
また、型に指定された文字列は、内部で以下の4つのいずれかとして解釈される。
| 型名 | 型が指定された列に保存できる型 |
|---|---|
| INTEGER | INTEGER, REAL, TEXT, BLOB |
| REAL | REAL, TEXT, BLOB |
| TEXT | TEXT, BLOB |
| BLOB | INTEGER, REAL, TEXT, BLOB |
これは、各データを挿入する際の変換規則にも通じる。例えばINTEGERやREALが指定されたカラムには文字列を入れられるが、TEXTが指定されたカラムに整数を入れようとすると、文字列に変換される。
以下は、それぞれのデータ型の変換ルールを確かめるためのサンプルコマンドである。
テーブルの作成
テーブルの作成には、CREATE TABLEコマンドを使う。
以下は、型アフィニティを指定せずにテーブルを作成するコマンドである。
列に型アフィニティを指定
型アフィニティを指定する場合は、列名の後ろに空白を開けて以下のように書く。
なお、テーブル作成時には、INTEGER、REAL、TEXT、BLOBの4タイプに加えてNUMERICという型も指定できる。この型は、挿入されるデータをまずINTEGERに変換し、無理ならREALに寄せる。
制約
制約(Constraints)とは、列に保存できる値に定めるルールである。制約と誓約の制約だ。列に制約を掛けるには、以下のようなキーワードを型アフィニティの後ろにつける。型アフィニティを指定しない場合は列名の後ろにつける。
| 制約の種類 | 概要 |
|---|---|
PRIMARY KEY |
重複する値を保存できず、NULLも保存できない……はずだがSQLiteではできるので注意。 |
NOT NULL |
NULLを保存できない。 |
UNIQUE |
重複する値を保存できないが、NULLは保存できる。 |
CHECK (式) |
条件に適う値しか保存できない。 |
DEFAULT 数値DEFAULT '文字列'DEFAULT (式)> |
値が指定されない場合、デフォルト値が保存される。 |
COLLATE 照合に使う関数名 |
並び替えなど、2つの文字列を比較する際に使う関数を指定する。 |
REFERENCES テーブル名 (列名1,..) |
自他のテーブルを問わないが、既存の列の値しか保存できない。 |
DEFAULTやCOLLATEは果たして制約なのかと思うが、この辺りは黙って飲み込んだほうがいい。
列に主キーを指定
主キーとは、その列に保存される値が一意である(重複がない)ことを保証する制約である。なんらかのIDに使われることが多い。基本的に、主キーは1つのテーブルに1つだけ指定できる。
以下は、primary_col列に整数型の主キーの働きをエラーメッセージで雑に確かめるサンプルコマンドである。
SQLiteでは、INTEGER PRIMARY KEYでオートインクリメントが発動する。これは、該当列に値を指定しない場合に、自動でインクリメントした値を挿入してくれる仕組みである。
また、標準SQLでは、主キーの列にはNOT NULL制約が掛かるが、以下のケースを除きSQLiteではNULL値を挿入できる。
- 列がINTEGER PRIMARY KEY
- 列がNOT NULL
- STRICTテーブル
- WITHOUT ROWIDテーブル
また、INTEGER PRIMARY KEYを指定した列は、rowid列のエイリアスとなる。
rowidとは
WITHOUT ROWIDテーブルを除き、SQLiteのテーブルの全行は、その行を特定するための64ビットの符号つき整数をID(=rowid)を持つ。この列は、列定義に指定しなくても自動で作られる。
ただ、SELCT * FROM table_name;など、列名を明示しない形では参照できない。参照する場合は、rowid、oid、_rowid_のいずれかを使う。
もし同名の列を定義している場合は、そちらが優先される。
ちなみに"特定のrowidを持つレコード、または指定された範囲内のrowidを持つすべてのレコードを検索する場合、他のPRIMARY KEYやインデックス値を指定して同様の検索を行うよりも約2倍高速"だそうである。
参考:ROWIDs and the INTEGER PRIMARY KEY
複数の列で主キーを指定
複数の列の組み合わせを主キーにすることもできる。この場合、列定義の丸括弧内の末尾に、PRIMARY KEY (col1, ...)を付ける。
NULLを許さない
NOT NULLを使う。
重複を許さない
UNIQUEを使う。PRIMARY KEYとの主な違いは、NULLを許すことと、1つのテーブル内で複数の列に指定できる点にある。
保存できる値を条件で絞る
列に保存できる値に条件を付与するには、CHECK (式)を使う。この式の結果は数値にキャストされ、結果が0(整数値0または実数値0.0)の場合に違反と判断される。
任意の列の値のみを許す
ある列の値を、別の列の値(親子関係を持つデータのidなど)のみに制限したい場合は、外部キー制約を使う。ただし、SQLiteでは、この制約はデフォルトで無効になっている。
そのため、使う前に有効化する必要がある。
上記のコマンドでは、1つ目のINSERTコマンドは成功し、2つ目のINSERTコマンドは違反する。1つ目のINSERTのように、同じコマンドの中であっても、制約に指定した列に値が入っていれば問題はない。
列単位ではREFERENCES テーブル名(列名1,...)を使うが、テーブル単位の制約もできる。その場合、列定義の丸括弧の末尾にFOREIGN KEY(列名1,...) REFERENCES テーブル名(列名1,...)と書く。
サンプルでは同じテーブルの列の値を指定しているが、別のテーブルの列の値を指定してもよい。
列にデフォルト値を指定
列にデフォルト値を指定するには、型アフィニティの後ろにDEFAULT句を追加する。デフォルト値は、INSERTコマンドで列への値が省略された場合に使われる。
TIMESTAMPというデータ型を指定することで、デフォルト値に以下の特別なキーワードも指定できる。これらは定数であるので、「’」や「"」は不要である。
| デフォルト値の定数 | 概要 |
|---|---|
| CURRENT_TIME | 現在時刻。形式は"HH:MM:SS"。 |
| CURRENT_DATE | 現在日時。形式は"YYYY-MM-DD"。 |
| CURRENT_TIMESTAMP | 現在のタイムスタンプ。形式は"YYYY-MM-DD HH:MM:SS"。 |
照合順を指定する
文字列の照合順の指定には、COLLATEを使う。
| 照合関数 | 概要 |
|---|---|
| BINARY | エンコーディングに関係なく、memcmp()を使用。 |
| NOCASE | sqlite3_strnicmp()を使用。バイナリと似ているが、大文字小文字を区別しない。 |
| RTRIM | 末尾のスペース文字を無視する。 |
任意の列の値のみを許す
ある列の値を、別の列の値(親子関係を持つデータのidなど)のみに制限したい場合は、外部キー制約を使う。ただし、SQLiteでは、この制約はデフォルトで無効になっている。
そのため、使う前に有効化する必要がある。
上記のコマンドでは、1つ目のINSERTコマンドは成功し、2つ目のINSERTコマンドは違反する。1つ目のINSERTのように、同じコマンドの中であっても、制約に指定した列に値が入っていれば問題はない。
列単位ではREFERENCES テーブル名(列名1,...)を使うが、テーブル単位の制約もできる。その場合、列定義の丸括弧の末尾にFOREIGN KEY(列名1,...) REFERENCES テーブル名(列名1,...)と書く。
サンプルでは同じテーブルの列の値を指定しているが、別のテーブルの列の値を指定してもよい。
テーブルに厳格な型チェックを指定
version 3.37.0 (2021-11-27)以降では、テーブル単位で厳格な型チェックを行うテーブル(=STRICTテーブル)を作成できる。
以下のように、CREATE TABLEコマンドのお尻にSTRICTキーワードをつけるだけでいい。
STRICTテーブルを作成する際は、各列に以下のいずれかのデータ型を指定する必要がある。
- INT
- INTEGER
- REAL
- TEXT
- BLOB
- ANY
これ以外の型を指定したり、省略したりするとパースエラーが出る。
また、カラムに指定されたアフィニティに変換できないデータを保存しようとすると、ラインタイムエラーが出る。変換できる場合は、ぬるりと保存される。
テーブルの削除
テーブルの削除には、DROP TABLEコマンドを使う。
テーブルに関する変更
テーブルに関する変更は、ALTER TABLEというコマンドと任意のサブコマンドの組み合わせて行う(‘alter’は部分的に変更する、修正するという意味の動詞である)。
テーブル名の変更
テーブル名を変更する場合は、以下のようにRENAME TOというサブコマンドを使う。
列の追加
列の追加には、ADD COLUMNというサブコマンドを使う。
列名の変更
列名の変更には、RENAME COLUMN TOというサブコマンドを使う。
列の削除
列の削除には、DROP COLUMNというサブコマンドを使う。
型アフィニティの確認
ドットコマンドの.schemaを使って、該当のテーブルのSQLコマンドで確認する。
型アフィニティの変更
カラムに指定した型アフィニティを変更することはできない。
新しくテーブルを作成し直し、データを丸ごと挿入し直すことで、同様の結果を得るしかない。
インデックス
今後追記予定
ビュー
今後追記予定