Типы данных в SQLite: полное руководство по классам хранения и аффинитивности
SQLite — это лёгкая база данных, которая хранит все данные в одном файле. Её ключевая особенность — гибкая система типов. В отличие от MySQL или PostgreSQL, где тип данных столбца задаётся жёстко, SQLite использует динамическую типизацию.
Принцип работы типов в SQLite
В SQLite не нужно указывать тип данных для столбцов при создании таблицы. Вместо этого SQLite использует динамическую типизацию, то есть присваивает тип каждому вставленному значению на основе его содержимого.
Тем не менее при создании столбцов в таблицах SQLite можно указывать типы данных. Это служит подсказкой для базы данных о том, какой тип данных следует ожидать.
При этом SQLite может хранить данные любого типа в любом столбце, независимо от объявленного типа.
Классы хранения в SQLite
В SQLite каждое отдельное значение (а не столбец таблицы) принадлежит к одному из пяти классов хранения (Storage Class). Это физический формат хранения данных в файле БД:
NULL: ЗначениеNULL.INTEGER: Целое число со знаком.REAL: Число с плавающей запятой.TEXT: Текстовая строка.BLOB: Последовательность байтов (бинарные данные), хранимая "как есть".
Аффинитивность типов
Аффинитивность типа — это не свойство значения, а свойство столбца. Это рекомендация для SQLite о том, в каком формате предпочтительнее хранить данные в этом столбце.
Аффинитивность не является жёстким ограничением. В столбец с любой аффинитивностью можно вставить значение любого класса хранения. Однако, если это значение имеет подходящий формат, SQLite попытается его преобразовать в соответствии с аффинитивностью столбца.
SQLite определяет пять видов аффинитивности столбца:
TEXT: Данные в столбце хранятся в текстовом формате.NUMERIC: Столбец предпочитает числовое хранение (INTEGERилиREAL), но может принять иTEXT.INTEGER: Ориентирован на хранение целых чисел.REAL: Столбец предпочитает хранение в виде числа с плавающей запятой.BLOB: Для значений в этом столбце преобразования не производятся.
При создании столбца можно указать аффинитивность с типом, чтобы дать SQLite подсказку о типе данных, предполагаемых к хранению в этом столбце. Затем SQLite будет использовать эту информацию для определения класса хранения значений в этом столбце.
Важно помнить: SQLite позволяет хранить данные любого типа в любом столбце, независимо от объявленного типа.
Допустим, вы создали следующую таблицу:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
score REAL
);В этой таблице столбец id должен хранить целочисленные значения, столбец name — текстовые, а столбец score — значения с плавающей точкой. Однако можно вставить текстовое значение в столбец score, и SQLite сохранит его как текстовое значение без каких-либо проблем.
INSERT INTO users (id, name, score) VALUES (1, 'Alice', 'unknown');
SELECT * FROM users WHERE id = 1;Это вернёт
1 | Alice | unknownПравила аффинитивности типов в SQLite
При вставке или обновлении значения SQLite применяет следующие правила преобразования, основанные на аффинитивности целевого столбца:
INTEGER: Значение хранится как целое число, если оно может быть преобразовано в целое число без потери информации. Если значение не может быть преобразовано в целое число, SQLite будет хранить его как вещественное значение.REAL: Значение хранится как вещественное, если оно может быть преобразовано в вещественное без потери информации. Если значение не может быть преобразовано в вещественное, SQLite будет хранить его как текст.TEXT: Значения хранятся как текстовые.NUMERIC: SQLite преобразует и сохраняет значение как целое или вещественное, если это возможно. Если преобразование невозможно, значение сохраняется какTEXT.BLOB: значения хранятся в видеBLOB(исторически этот вид аффинитивности называлсяNONE, но позже был переименован).
Эти правила применяются последовательно и определяют, как исходный класс хранения значения может измениться перед сохранением в столбце.
В таблице ниже показано, как SQLite обрабатывает разные типы значений при вставке в столбец с аффинитивностью INTEGER:
| Исходное значение (и его класс хранения) | Что происходит при вставке в INTEGER столбец | Итоговый класс хранения в столбце |
|---|---|---|
42 (INTEGER) | Сохраняется как есть | INTEGER |
'42' (TEXT) | Конвертируется в целое число | INTEGER |
3.14 (REAL) | Не конвертируется (теряется дробная часть) → сохраняется как REAL | REAL |
'Hello' (TEXT) | Не конвертируется → сохраняется как REAL | REAL |
x'0012FF' (BLOB) | Не конвертируется → сохраняется как REAL | REAL |
Ключевой вывод: Аффинитивность INTEGER стремится к целочисленному хранению, но в случае неудачи преобразования в целое число, значение сохраняется как REAL. Это ключевое отличие аффинитивности INTEGER от NUMERIC, которая может откатиться и к TEXT.
Сравнение классов хранения и аффинитивности:
| Концепция | Класс хранения | Аффинитивность типа |
|---|---|---|
| Что это? | Физический формат хранения данных в файле БД | «Предпочтение» столбца к формату данных (подсказка для SQLite) |
| К чему относится? | Отдельное значение в ячейке | Столбец таблицы |
| Пример | 42 (INTEGER), 'Привет' (TEXT) | CREATE TABLE t (id INTEGER, name TEXT); |
Как SQLite определяет аффинитивность по имени типа
Хотя у SQLite всего пять видов аффинитивности, при объявлении типа столбца можно использовать множество имён-псевдонимов. SQLite определяет аффинитивность, сопоставляя название типа с шаблонами:
INTEGER: любой тип со строкойINT.- Например,
INT,INTEGER,SMALLINT.
- Например,
TEXT: любой тип со строкамиCHAR,CLOBилиTEXT.- Например,
CHARACTER,VARCHAR,NCHARи т. д.
- Например,
BLOB: любой тип со строкойBLOB.REAL: любой тип со строкамиREAL,FLOAилиDOUB.- Например,
FLOAT,DOUBLEи т. д.
- Например,
NUMERIC: всё, что не соответствует вышеперечисленным.
Какие типы SQLite не поддерживает
В отличие от других СУБД, SQLite не имеет встроенных типов DATE,BOOLEAN и подобных. Однако вы можете хранить такие данные, используя один из пяти классов хранения.
К типам не поддерживаемым SQLite относятся:
BOOLEAN/BITDATETIMEDATETIME/TIMESTAMP
Работа с датами в SQLite
Хотя в SQLite нет отдельного типа DATE, даты можно хранить, используя один из классов хранения: TEXT (формат ISO-8601), INTEGER (временная метка Unix) или REAL (число дней юлианского календаря).
Как правило, для дат лучше использовать человекочитаемый формат ISO-8601 (например, 2024-09-01 01:48:00), поскольку этот формат наиболее широко поддерживается и с ним легко работать в SQLite.
Ниже приведены примеры того, как можно работать с датами в SQLite:
-- Формат ISO-8601, хранящийся как TEXT (YYYY-MM-DD HH:MM:SS)
INSERT INTO events (name, date) VALUES ('Birthday', '2024-09-01');
INSERT INTO events (name, date) VALUES ('Appointment', '2024-09-01 01:48:00');
-- Временная метка Unix, хранящаяся как INTEGER (целое число секунд с 1970-01-01)
INSERT INTO events (name, date) VALUES ('Meeting', 1723772054);
-- Число дней юлианского календаря, хранится как REAL (Дни с полудня в Гринвиче 24 ноября 4714 года до н.э.)
INSERT INTO events (name, date) VALUES ('Conference', 2459786.574074);Для работы с датами в этих форматах SQLite предоставляет набор встроенных функций, таких как date(), time() и strftime(). Например, с их помощью можно извлекать компоненты даты или форматировать вывод.
SQLite определяет, как интерпретировать значение даты, по его классу хранения: значения типа REAL рассматриваются как числа юлианского дня, INTEGER — как временные метки Unix, а TEXT — как строки в формате ISO-8601.
Сравнение типов данных SQLite с другими СУБД
Чтобы лучше понять уникальность подхода SQLite, сравним его систему типов с двумя популярными СУБД:
| Характеристика | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| Подход к типам | Динамическая типизация (тип у значения) | Статическая типизация (тип у столбца) | Строгая статическая типизация |
| Основные типы | 5 классов хранения: NULL, INTEGER, REAL, TEXT, BLOB | Более 20 типов: INT, VARCHAR, DATE, BOOL, ENUM, SET и др. | Богатая система типов: INT, VARCHAR, DATE, JSON, ARRAY, UUID, гео-типы |
| Гибкость | Можно хранить любой тип в любом столбце | Жёсткие ограничения по типам столбцов | Строгие ограничения, но с поддержкой кастов и преобразований |
| Дата/Время | Нет встроенного типа. Хранятся как TEXT, INTEGER или REAL | DATE, TIME, DATETIME, TIMESTAMP, YEAR | DATE, TIME, TIMESTAMP, INTERVAL, timetz, timestamptz |
| Логический тип | Нет встроенного BOOLEAN | BOOL/BOOLEAN (синоним TINYINT(1)) | BOOLEAN с true/false |
| Перечисления | Нет поддержки ENUM | ENUM('val1', 'val2') | ENUM через CREATE TYPE |
| Двоичные данные | BLOB (единый тип) | BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB | BYTEA, Large Objects |
| Числовые типы | INTEGER, REAL (масштабируемые) | TINYINT, INT, BIGINT, DECIMAL, FLOAT, DOUBLE | SMALLINT, INTEGER, BIGINT, DECIMAL, NUMERIC, REAL, DOUBLE PRECISION |
| Текстовые типы | TEXT (единый, до 1ГБ) | CHAR, VARCHAR, TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT | CHAR, VARCHAR, TEXT (неограниченный) |
| JSON | Поддержка через расширения и функции JSON1 | JSON (с версии 5.7) | JSON, JSONB (бинарный, с индексацией) |
| Массивы | Не поддерживаются | Не поддерживаются | INT[], TEXT[] и другие массивы |
| Производительность | Быстрее для простых операций, меньше накладных расходов | Оптимизирована под сложные запросы и большие объёмы | Оптимизирована под сложные операции и ACID |
| Использование | Встраиваемые системы, мобильные приложения, локальное хранение | Веб-приложения, средние/большие проекты | Сложные приложения, аналитика, геоданные, enterprise |
| Преобразование типов | Автоматическое (основано на аффинитивности) | Строгое с возможными предупреждениями/ошибками | Строгое с явным приведением (CAST, ::) |
| Пример объявления | CREATE TABLE t (id ANY, name ANY); | CREATE TABLE t (id INT, name VARCHAR(100)); | CREATE TABLE t (id INTEGER, name TEXT); |
Ключевые выводы из сравнения
- SQLite — максимально гибкий: Один столбец может хранить разные типы данных, что упрощает прототипирование
- MySQL — баланс гибкости и структуры: Поддерживает много типов, но сохраняет строгость объявлений
- PostgreSQL — максимальная строгость и богатство: Самая развитая система типов с дополнительными возможностями (массивы, JSONB, пользовательские типы)
Практические аспекты производительности
Сравнение подходов к типам данных напрямую влияет на производительность в разных сценариях:
| Операция / Сценарий | SQLite | MySQL | PostgreSQL | Объяснение различий |
|---|---|---|---|---|
| Вставка 1000 записей | ⚡⚡⚡ Очень быстро (меньше проверок типов) | ⚡⚡ Быстро (оптимизированный bulk insert) | ⚡ Быстрее в последних версиях | SQLite меньше проверяет типы при вставке |
SELECT с фильтрацией по типу | ⚡⚡ Хорошо | ⚡⚡⚡ Отлично (специализированные индексы) | ⚡⚡⚡ Отлично (расширенные индексы) | Статическая типизация позволяет лучшую оптимизацию |
| Изменение типа столбца | ⚡⚡⚡ Тривиально (динамическая типизация) | ⚡ Затратно (ALTER TABLE пересоздаёт таблицу) | ⚡⚡ Проще чем MySQL (но требует времени) | SQLite фактически не меняет тип, а меняет аффинитивность |
| Хранение смешанных типов в столбце | ⚡⚡⚡ Нативно поддерживается | ❌ Требует TEXT/VARCHAR | ❌ Требует TEXT/VARCHAR | Динамическая типизация — ключевое преимущество SQLite |
| Сортировка числовых значений | ⚡⚡ Быстро, но может требовать явного CAST | ⚡⚡⚡ Быстро (тип известен заранее) | ⚡⚡⚡ Быстро (оптимизированные алгоритмы) | SQLite сортирует как текст, если значения хранятся как TEXT |
| Работа с датами | ⚡ Умеренно (функции вычисляются) | ⚡⚡⚡ Быстро (специализированный тип) | ⚡⚡⚡ Быстро (богатые временные функции) | Встроенные типы DATE оптимизированы для операций |
| Индексация по типу | ⚡⚡ Универсальные индексы | ⚡⚡⚡ Специализированные (FULLTEXT, пространственные) | ⚡⚡⚡ Расширенные (GIN, GiST для JSON/массивов) | Богатые системы типов = больше вариантов индексации |
| Объём занимаемой памяти | ⚡⚡⚡ Минимальный (гибкое хранение) | ⚡⚡ Эффективный (но фиксированные размеры) | ⚡ Эффективный (но overhead для сложных типов) | SQLite экономит память за счёт динамического определения типа |
Ключевые выводы о производительности
- SQLite выигрывает в гибкости и простоте: Меньше проверок типов = быстрее простые операции
- MySQL и PostgreSQL выигрывают в сложных сценариях: Предсказуемость типов = лучшая оптимизация запросов
- Для смешанных данных SQLite вне конкуренции: Только SQLite позволяет хранить числа, текст и даты в одном столбце без преобразований
- Специализированные операции быстрее со статической типизацией: Сортировка, поиск по датам, индексация
Рекомендации по выбору на основе производительности
| Требование к производительности | Лучший выбор | Причина |
|---|---|---|
| Быстрое прототипирование с меняющейся структурой | SQLite | Не нужно пересоздавать таблицы при изменении типов |
| Высокая нагрузка на чтение с сложными запросами | PostgreSQL | Оптимизатор запросов лучше работает со статическими типами |
| Много операций вставки/обновления | MySQL или SQLite | Зависит от структуры данных |
| Работа с геоданными или полнотекстовым поиском | PostgreSQL или MySQL | Специализированные типы и индексы |
| Ограниченные ресурсы (память, диск) | SQLite | Минимальный overhead на хранение типов |
Когда что выбирать
| Сценарий | Рекомендуемая СУБД | Почему |
|---|---|---|
| Мобильное приложение | SQLite | Лёгкость, встраиваемость, один файл |
| Быстрый прототип | SQLite | Не нужно заранее определять точные типы |
| Веб-приложение средней сложности | MySQL | Баланс производительности и возможностей |
| Сложное enterprise-приложение | PostgreSQL | Богатые типы данных, JSON, расширяемость |
| Аналитика и отчёты | PostgreSQL | Поддержка сложных запросов и оконных функций |
Заключение
Теперь вы знакомы с классами хранения (TEXT, INTEGER, REAL, BLOB и NULL) и аффинитивностью типов (TEXT, NUMERIC, INTEGER, REAL и BLOB), используемыми в системе динамических типов SQLite, а также с взаимосвязью между ними. Важно понимать: аффинитивность типа — это лишь рекомендация. SQLite сохранит в столбец данные любого класса хранения, независимо от объявленной аффинитивности.
Рекомендации по выбору аффинитивности типа:
| Что хотите хранить | Рекомендуемая аффинитивность | Класс хранения |
|---|---|---|
| Текст | TEXT | TEXT |
| Целые числа | INTEGER | INTEGER |
| Дробные числа | REAL или NUMERIC | REAL |
Часто задаваемые вопросы
❓ В чём главное отличие SQLite от других СУБД в работе с типами данных?
SQLite использует динамическую типизацию, тогда как большинство других СУБД (MySQL, PostgreSQL) используют статическую типизацию. Это означает, что в SQLite тип данных привязан к значению, а не к столбцу.
❓ Может ли это привести к проблемам с данными?
Да, если не быть внимательным. Например, если хранить текстовые значения в столбце с аффинитивностью INTEGER, могут возникнуть проблемы при математических операциях. Рекомендуется следовать объявленным типам для предотвращения ошибок.
❓ Как правильно выбрать аффинитивность для столбца?
- Используйте
TEXTдля строк и дат в формате ISO-8601 - Используйте
INTEGERдля целых чисел и временных меток Unix - Используйте
REALдля дробных чисел и дат в формате юлианских дней - Используйте
BLOBдля бинарных данных, которые не должны преобразовываться
❓ Почему SQLite сохраняет unknown в REAL столбце?
Это демонстрирует гибкость SQLite: хотя столбец объявлен как REAL, SQLite позволяет хранить там любой класс хранения. На практике так делать не рекомендуется.
❓ Есть ли разница между BIGINT, INT и INTEGER в SQLite?
Нет, нет разницы. Все эти объявления дают столбцу аффинитивность INTEGER. SQLite использует сопоставление по шаблону: любое имя типа, содержащее "INT", получает аффинитивность INTEGER.
❓ Как лучше хранить булевы значения в SQLite?
Рекомендуется использовать INTEGER с значениями 0 (false) и 1 (true). Или TEXT со значениями TRUE/FALSE, если важна читаемость.
❓ Можно ли изменить аффинитивность существующего столбца?
Прямо — нет. Нужно создать новую таблицу с нужной структурой, скопировать данные и удалить старую таблицу. Или использовать ALTER TABLE ... RENAME COLUMN (в новых версиях SQLite).
❓ Влияет ли аффинитивность на производительность?
Косвенно — да. Правильная аффинитивность позволяет SQLite эффективнее использовать индексы и выполнять операции. Например, сравнение чисел в INTEGER столбце быстрее, чем в TEXT.