やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3学習 SQLiteのクセ

 これは把握しておくべき。

情報源

クセ

  • クライアントサーバーでない
  • 型がゆるゆる
  • 外部キーはデフォルトでオフ
  • 主キーはNULL許容
  • GROUP BY句にない非集計結果列を含めることができる
  • 大文字と小文字は区別されない(デフォルト)
  • ダブルクォートで囲まれた文字列リテラルを許容
  • キーワードを識別子として使える
  • エラーや警告なしに酷いSQLが許される
  • AUTOINCREMENTMySQLと同じように機能しない

クライアントサーバーでない

  • 同時書込できない
  • 整合性の維持には排他制御が必要

 ようするにSQLite3は「スレッドセーフでない」。ファイルシステムの問題ともいえる。

型がゆるゆる

 データ型NULL,INTEGER,REAL,TEXT,BLOBの5種類のみ。

  • INTEGER型にABCなど文字列をセットできてしまう(エラーも出さない)
  • VARCHAR(50)のように長さ制限してもそれ以上の長さのデータを保存できてしまう

 ようするにSQLite3は「バリデーションできない」。一体なんのための型なのか。

固有の型はない

 さらに一般的によく使うであろう以下の型もない。

  • Boolean
  • Date, Time, DateTime

 INTEGERTEXTで代用することになる。プログラムでは自前で型変換が必要になる。ORマッパーと型変換定義が必要。

外部キーはデフォルトでオフ

SQLでONにする方法

PRAGMA foreign_keys = boolean;

ビルドオプションでONにする方法

SQLITE_DEFAULT_FOREIGN_KEYS=<0 or 1>

主キーはNULL許容

PRIMARY KEYの列値はNULLにすることができます。これはバグですが、この問題が発見された頃には、バグに頼っていた膨大な数のデータベースがそこで発見されたため、バグの振る舞いをサポートすることを決定しました。

 Rustのdieselprimary keyを指定したにもかかわらずNullableになったのはおかしいと思っていた。SQLite3のバグ仕様だったのか。これはやめて欲しい。

 これもう標準SQLじゃないのでは?

GROUP BY句にない非集計結果列を含めることができる

1. min(), max()を含むなら他の列はそれに該当する値とする

SELECT max(salary), first_name, last_name FROM employee;

 salaryが最大の行であるfirst_name, last_nameを表示する。

2. 重複削除

 DISTINCT ON句の代わりにGROUP BY句を使える。

クエリに集計関数がまったく含まれていない場合は、DISTINCT ON句の代わりにGROUP BY句を追加できます。つまり、GROUP BY句の個々の値のセットに対して1行だけが表示されるように、出力行がフィルタ処理されます。それ以外の場合に2つ以上の出力行がGROUP BY列に対して同じ値のセットを持っていた場合は、いずれかの行が任意に選択されます。

大文字と小文字は区別されない(デフォルト)

 大文字と小文字の変換をするなら以下。

  1. コンパイルオプション-DSQLITE_ENABLE_ICU付与
  2. ICUライブラリをリンクする

ダブルクォートで囲まれた文字列リテラルを許容

 標準SQLでは以下。

クォート 意味
シングルクォート 文字列リテラル
ダブルクォート 識別子

 ただし、MySQL3.xとの互換性を保つためにダブルクォートを文字列リテラルとして解釈することもできる。

キーワードを識別子として使える

CREATE TABLE union(true INT, with BOOLEAN);

 上記コードにおけるテーブル名unionはキーワードである。ふつうは"union"のようにダブルクォートで囲ってやらねばならない。だが、SQLite3は文脈によってクォートがなくとも識別子だと判断する。

エラーや警告なしに酷いSQLが許される

SQLiteの最初の実装は、「あなたが受け入れるものにはリベラルであること」という部分を述べたポステルの法則に従うように努めました 。これは、良い設計と考えられていました - システムは危険な入力を受け入れ、あまり文句を言うことなくできる限り最善を尽くします。しかし最近では、入力の間違いを見つけやすくするために、受け入れている内容に厳密を置くことの方が良い場合があることに気づくようになりました。

 で、詳細なエラーは実装されているの?

AUTOINCREMENTMySQLと同じように機能しない

 どこが違うのかはわからなかった。

対象環境

前回まで