Пять классов хранения вместо десятков типов
В SQLite любое значение хранится в одном из пяти классов хранения (storage classes):
NULL— отсутствие значения.INTEGER— целое число со знаком, занимает от 1 до 8 байт в зависимости от величины.REAL— число с плавающей точкой по стандарту IEEE, 8 байт.TEXT— строка в кодировке базы (как правило, UTF-8).BLOB— сырые байты, сохраняются ровно в том виде, в котором вы их передали.
И всё. Никакого отдельного BOOLEAN, DATETIME, VARCHAR или DECIMAL. В других СУБД типов данных — десятки, а в SQLite их всего пять, и все остальные «типы» строятся поверх них.
typeof() показывает реальный класс хранения каждого значения. Вы увидите integer, real, text, blob. Эти четыре плюс null — всё, что вообще знает SQLite.
Динамическая типизация SQLite
А теперь то, что обычно удивляет тех, кто пришёл из Postgres или MySQL. В SQLite (без режима STRICT) тип, который вы указываете у колонки, — это скорее пожелание, чем обязательство. Настоящий тип хранится вместе с самим значением:
Обе строки были приняты без вопросов. В колонке id в одной строке лежит целое число, в другой — текст; в body — наоборот, текст и число. SQLite спокойно хранит значения любого класса в любой колонке.
Это и есть динамическая типизация, и сделано так намеренно. Благодаря ей SQLite прощает многое — удобно для прототипов и быстрых скриптов. Но та же особенность означает, что опечатка в коде приложения может годами тихо записывать данные не того типа. Если такой компромисс вас не устраивает — а для большинства продакшен-схем так и должно быть — на помощь приходят таблицы STRICT. До них мы скоро доберёмся.
Аффинность типов в двух словах
Объявленный тип колонки не игнорируется — он задаёт ей аффинность (type affinity). Когда вы вставляете значение, SQLite пытается привести его к аффинности колонки, если преобразование проходит без потерь. Колонка TEXT, в которую попало число 42, сохранит его как текст '42'; колонка INTEGER, получившая строку '42', запишет её как целое 42. А вот если преобразование грозит потерей информации, исходный тип сохраняется как есть.
Первая строка: целое 42 приведено к тексту '42', а строка '100' — к числу 100. Вторая строка: '3.5' нельзя без потерь конвертировать в INTEGER, поэтому значение осталось текстом. Про affinity будет отдельный разговор чуть позже — пока запомните главное: тип колонки всё-таки влияет на то, как хранятся данные, даже если жёстко его не навязывает.
Тип boolean в SQLite
Отдельного класса хранения BOOLEAN в SQLite попросту нет. Булевы значения хранятся как целые числа: 0 — это false, 1 — true:
Ключевые слова TRUE и FALSE распознаются (начиная с SQLite 3.23) и превращаются в 1 и 0. Объявление BOOLEAN даёт колонке числовую аффинность, но не ограничивает её значениями 0/1 — без режима STRICT вы спокойно вставите туда 'maybe', и SQLite даже не пикнет.
Дата и время в SQLite
Типа DATETIME тут тоже нет. Вместо него выбираете один из трёх вариантов хранения, и встроенные функции работы с датами умеют со всеми тремя:
TEXTв формате ISO-8601:'2026-04-23 14:30:00'.REAL— юлианский день.INTEGER— секунды Unix-эпохи.
Текст в формате ISO-8601 — самый популярный вариант: он корректно сортируется как строка, читается человеком, да и все встроенные функции (date(), time(), datetime(), strftime(), julianday()) умеют с ним работать. Выберите один формат на колонку и придерживайтесь его — смешивать разные представления в одном столбце означает нарваться на проблемы через полгода.
VARCHAR, CHAR и другие знакомые имена
SQLite принимает привычные по другим СУБД названия типов — VARCHAR(255), CHAR(10), NVARCHAR, DECIMAL(10,2), DOUBLE, FLOAT, INT, BIGINT, MEDIUMINT. Парсятся они без проблем. Просто по правилам аффинности каждое из них сводится к одному из пяти классов хранения.
VARCHAR(255) не ограничивает длину 255 символами — SQLite просто игнорирует это число. DECIMAL(10, 2) не хранит число с фиксированной точностью — колонка получает numeric affinity, а данные ложатся как INTEGER или REAL. Эти имена оставлены лишь для того, чтобы схемы, скопированные из других СУБД, не падали при запуске, — но связанных с ними ограничений в SQLite нет.
Если нужна точная арифметика для денег, храните центы (или копейки) как INTEGER. С REAL рано или поздно вылезут ошибки округления уже в третьем знаке после запятой.
NULL — это отдельный класс хранения
NULL — не просто «нет значения», а полноценное значение со своим классом хранения, который возвращает typeof():
b показывается как null. Это важный момент: NULL не равен ничему — даже другому NULL. Условие b = NULL никогда не истинно, нужно писать b IS NULL. Подробнее об этом — в разделе про операторы и NULL, но начинается всё именно здесь, с класса хранения.
Хранение байтов в типе BLOB
Тип данных BLOB хранит сырые байты как есть — удобно для небольших картинок, хэшей, закодированных данных, в общем, для всего, что не является текстом или числом:
Литерал x'...' позволяет записывать блобы в SQL в шестнадцатеричном виде; из кода приложения обычно удобнее передавать массив байт через параметр. length() для блоба возвращает количество байт, а не символов.
Практический момент: SQLite без проблем хранит большие блобы, но тянуть 50-мегабайтный блоб через каждый запрос, который трогает строку, — медленно. Для больших файлов лучше держать сам файл на диске, а в базе хранить путь к нему.
Что стоит запомнить
- Пять классов хранения —
NULL,INTEGER,REAL,TEXT,BLOB— покрывают всё. - Булевы значения — это целые числа; даты можно хранить как текст, как REAL или как INTEGER (на ваш выбор).
- Объявленные типы колонок — это подсказки, а не жёсткие контракты (если только вы не используете
STRICT-таблицы). VARCHAR(255)и его собратья парсятся без ошибок, но длину и точность, которые они подразумевают в других СУБД, SQLite не контролирует.typeof(value)— ваш лучший друг, когда непонятно, что на самом деле лежит в колонке.
Дальше: type affinity
То «подсказочное» поведение, которое мы здесь упомянули мимоходом, на самом деле описано чёткими правилами — пять классов affinity, выводимых из имени объявленного типа и применяемых при каждой вставке. Об этом — следующая страница, и именно там ключ к тому, чтобы заранее понимать, что SQLite сделает с вашими значениями.
Часто задаваемые вопросы
Какие типы данных поддерживает SQLite?
В SQLite всего пять классов хранения: NULL, INTEGER, REAL, TEXT и BLOB. Каждое значение в базе хранится в одном из них. Привычные VARCHAR(255), DATETIME или BOOLEAN в CREATE TABLE тоже принимаются — это сделано для совместимости, — но при сохранении они всё равно сводятся к одному из этих пяти.
Есть ли в SQLite отдельный тип boolean или datetime?
Как отдельных классов хранения — нет. Булевы значения хранятся как INTEGER (0 и 1), хотя ключевые слова TRUE и FALSE SQLite тоже понимает. Дата и время хранятся одним из трёх способов: TEXT (строки в формате ISO-8601), REAL (юлианский день) или INTEGER (Unix-время в секундах). Кодировку выбираете вы сами, а встроенные функции для работы с датами корректно работают со всеми тремя вариантами.
Почему типизация в SQLite называется динамической?
В большинстве СУБД колонка с типом INTEGER просто не примет строку. В SQLite (по умолчанию) объявленный тип — это, скорее, подсказка: реальный тип хранится вместе со значением, поэтому в колонке TEXT спокойно может оказаться число, если вы его туда положите. Иногда это удобно, а иногда стреляет в ногу. Если такое поведение не нужно — используйте STRICT-таблицы, они эту вольность отключают.