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コマンドで確認する。
型アフィニティの変更
カラムに指定した型アフィニティを変更することはできない。
新しくテーブルを作成し直し、データを丸ごと挿入し直すことで、同様の結果を得るしかない。
インデックス
今後追記予定
ビュー
今後追記予定