D1に備えて学んでおこうかと。
はっきり言って、他のデータベースについても全然よく知らない。が、SQLiteだけの特徴みたいなものがあるなら、知ってて損はないだろうというモチベーション。
SQLiteのドキュメントの中に、それ用のページがあったので軽くまとめていく。
この記事を書いてる時点でわかったD1の挙動に関することは⛅マークを付けてある。(SQLiteのデフォルトと違うものなど)
1. Overview
- SQLはスタンダードな言語でありつつ、その挙動はDBによって違う
- そういうもんなので、割り切りつつも、このページを読もうね
あ、はい。
2. SQLite Is Embedded, Not Client-Server
- SQLiteは埋め込み用途で、他のデータベースとは違う
- いわゆるクラサバ用途での利用を想定してない
と本家は言っているけど、Cloudflareさんがクラサバ用途に使うって言ってるんですよ・・。
3. Flexible Typing
- カラムに型は指定できるが、必須ではない
INTEGER
なカラムに"123"
を入れると、エラーにならず、123
にキャストしてくれる- SQLite用語ではAffinityという
- https://www.sqlite.org/datatype3.html#affinity
- ただし
"xyz"
の場合は、キャストできないので、"xyz"
のまま入る - その程度の弱い型付け
CREATE TABLE ... STRICT
とすれば、イメージ通りの強い型付けにもできる- ⛅ しかしD1は
STRICT
なテーブルには対応してなさそう - カラムの型に
BOOLEAN
はない INTEGER
で1
と0
または、TRUE
とFALSE
というキーワードが使えるDATETIME
もないので、TEXT
でISO-8601するかINTEGER
でUNIXタイムスタンプをいれよ
ORMからしかSQLを流さない場合は、あんまり関係ないかもしれんけど。
4. Foreign Key Enforcement Is Off By Default
- 外部キー制約はデフォルトでOFF(後方互換性のため)
PRAGMA foreign_keys = 1
などして有効にする- ⛅ D1はデフォルトで外部キー制約がONになってた
- ⛅
PRAGMA foreign_keys
にも対応してると書いてあるが、OFFにできなかった
ONでよさそう。
5. PRIMARY KEYs Can Sometimes Contain NULLs
- プライマリキーに
NULL
を設定できる - そして
NULL
とNULL
は重複扱いにならないので、ユニーク制約にもかからない - しかし
INTEGER PRIMARY KEY
とWITHOUT ROWID
する場合は例外 NULL
をいれると某Affinityが働いて、よしななINTEGER
になる
これもバグだとわかっているが、後方互換性のためにそうなってるとのこと。
6. Aggregate Queries Can Contain Non-Aggregate Result Columns That Are Not In The GROUP BY Clause
max()
みたいな集計関数を使うとき、GROUP BY
しなくても、他のカラムを返せるSELECT max(salary), first_name, last_name FROM employee
できるmin()
とmax()
の場合、複数の行が該当するかもしれないが、気まぐれに1行だけ返る
- 集計関数を使わない場合は、
GROUP BY
をDISTINCT ON
の代わりに使える - SQLiteは
DISTINCT
は使えるが、DISTINCT ON
はサポートしてない
なるほど。
7. Does Not Do Full Unicode Case Folding By Default
upper()
やlower()
といった関数に対応するのは、ASCII文字列だけ- すべてのユニコードをサポートしたい場合、自分でフラグ付けてコンパイルする
なるほど。
8. Double-quoted String Literals Are Accepted
- SQLiteのおいては、カラム名への
'
と、文字列への"
を使い分ける必要がない - MySQL 3.xとの互換性のため、
"
がカラムにマッチしなければ、文字列として解釈する
これも互換性のためにそうなってるとのこと。(そして後悔してる)
9. Keywords Can Often Be Used As Identifiers
UNION
やTRUE
などのキーワードも、テーブル名やカラム名に指定できるCREATE TABLE union(true INT, with BOOLEAN)
はエラーにならないってこと- ⛅ D1ではシンタックスエラーになる・・・!
- どうやらテーブル名にキーワードは使えないらしい
CREATE TABLE foo(INTEGER, TEXT TEXT)
は通った
まあそんなことせんやろうけど。
10. Dubious SQL Is Allowed Without Any Error Or Warning
- ポステルの法則(ロバストネス原則)に従って実装してきた
- が、最近のトレンドとして、柔軟に受け入れるより、厳密に間違いを示すほうがよいことに気づきつつある
え?ってなった。具体的な例はなかった。
11. AUTOINCREMENT Does Not Work The Same As MySQL
AUTOINCREMENT
はMySQLのそれとは異なる挙動をする- https://www.sqlite.org/autoinc.html
- SQLiteにおいて、
INTEGER PRIMARY KEY
なカラムはROWID
と同じ - その際、
INSERT
時に採番されるのは、その時点でのROWID
の最大値+1になる AUTOINCREMENT
の指定に関係なくそうなる- なので、同じ番号が再利用される可能性があるということ
AUTOINCREMENT
が指定されると、その再利用をしなくなる
- SQLiteにおいて、
そもそもAUTOINCREMENT
なんかいらんでしょ?無駄やしって書いてあった。
12. NUL Characters Are Allowed In Text Strings
- ヌル文字(ASCII
0x00
やユニコード\u0000
)を文字列の中に含むことができる - できるけど、おすすめはしないとのこと
どういうときに使うんやろう。
13. SQLite Distinguishes Between Integer And Text Literals
- SQLiteでは、
1='1'
はfalseになる
他のSQLでこれが通るのが理解できないって書いてあった。ポステルの法則はどこいったんや。
まとめ
あれやこれや、すごい正直なお気持ちが書かれたページだった。
とりあえずCloudflareさんは、D1側のドキュメントに、SQLite3との互換性をまとめてほしいです。