The 'lying about types' feature
By default, SQLite uses type affinity, not strict typing. A column declared INTEGER will prefer integer storage but will accept a string. A TEXT column will accept a number. This is intentional — it makes SQLite easy to embed in places where data shape is fluid (e.g., legacy CSV imports).
The five affinity classes:
- INTEGER — column types containing 'INT'.
- TEXT — types containing 'CHAR', 'CLOB', or 'TEXT'.
- BLOB — no affinity (column declared with no type, or with 'BLOB').
- REAL — types containing 'REAL', 'FLOA', or 'DOUB'.
- NUMERIC — anything else, including the ambiguous 'NUMERIC' name itself.
Affinity rules are quirky. Most of the time you can ignore them and just declare what you mean. But when surprises happen — a SUM coming back as a string, an index missing a row — the answer is almost always 'a value snuck in with the wrong storage class'.
Warning: Type affinity also affects how indexes and comparisons work.
WHERE id = '42' behaves differently than WHERE id = 42 if id has TEXT affinity. The fix is either STRICT mode (next lesson) or careful Python-side typing.