Виртуальная таблица в SQLite выглядит как обычная таблица с точки зрения SQL, но за ней стоит не стандартный файл базы, а пользовательский модуль. Когда вы выполняете SELECT, INSERT, UPDATE или DELETE, SQLite передаёт управление callback-методам этого модуля. За счёт этого таблица может читать CSV, работать с полнотекстовым поиском, отдавать статистику по файлам базы или вообще строить данные на лету

- Что такое виртуальная таблица
- Как создаётся виртуальная таблица
- Чем виртуальные таблицы отличаются от обычных
- Временные и eponymous virtual tables
- Где виртуальные таблицы особенно полезны
- Полнотекстовый поиск
- Пространственные запросы
- Служебная диагностика
- Как это работает внутри
- Когда использовать виртуальные таблицы в проекте
- Частые ошибки
Что такое виртуальная таблица
Обычная таблица хранит строки в файле базы данных SQLite. Виртуальная таблица использует интерфейс расширения и сама решает, откуда брать данные и как отвечать на запросы. Для SQL-клиента разница почти незаметна: таблицу можно читать, фильтровать, соединять и агрегировать так же, как обычную
Типовые сценарии:
- полнотекстовый поиск через FTS5
- пространственные индексы через R-Tree
- просмотр внутренней статистики базы через
dbstat - чтение CSV как таблицы
- доступ к внешнему источнику данных через SQL-слой
По этой теме полезно отдельно посмотреть EXPLAIN QUERY PLAN: план выполнения SQL-запроса в SQLite, чтобы расширить контекст и сравнить подходы
По этой теме полезно отдельно посмотреть Создание Flutter-приложения с SQLite, BLoC и Streams, чтобы расширить контекст и сравнить подходы
Как создаётся виртуальная таблица
Синтаксис выглядит так:
CREATE VIRTUAL TABLE docs USING fts5(title, body);
После USING указывается имя модуля и его аргументы. Именно модуль определяет поведение таблицы. В одном случае это будет полнотекстовый индекс, в другом — интерфейс к файловой системе или внешнему API
Во многих проектах это полезно, когда хочется не городить отдельный ETL-слой, а дать разработчикам привычный SQL-интерфейс поверх нестандартных данных
Чем виртуальные таблицы отличаются от обычных
Есть несколько ограничений, о которых стоит помнить:
- нельзя навесить обычный
CREATE INDEX, если сам модуль не поддерживает индексирование - нельзя использовать
ALTER TABLE ... ADD COLUMN - триггеры на виртуальные таблицы не создаются
- некоторые реализации поддерживают только чтение
Это ключевой trade-off. Вы получаете гибкость и возможность подключить новый источник данных к SQLite, но часть стандартных возможностей хранения переносится в логику модуля
Временные и eponymous virtual tables
SQLite поддерживает временные виртуальные таблицы и так называемые eponymous virtual tables. Во втором случае модуль можно использовать как таблицу без отдельного CREATE VIRTUAL TABLE
Например, так часто работает dbstat, когда модуль уже зарегистрирован в соединении:
SELECT name, path, pageno, pagetype
FROM dbstat
LIMIT 10;
Это удобно для диагностических и служебных модулей, где не хочется сначала заводить отдельную таблицу-обёртку
Где виртуальные таблицы особенно полезны
Полнотекстовый поиск
Самый частый реальный сценарий — FTS5. Вы получаете таблицу, которая снаружи выглядит как обычная, но внутри использует специализированный поисковый индекс
CREATE VIRTUAL TABLE articles_fts USING fts5(title, content);
INSERT INTO articles_fts(title, content)
VALUES ('SQLite WAL', 'Разбор режима write-ahead logging');
SELECT rowid, title
FROM articles_fts
WHERE articles_fts MATCH 'WAL';
Пространственные запросы
Через модуль rtree можно быстро искать объекты по диапазонам координат:
CREATE VIRTUAL TABLE geo_index USING rtree(
id,
minX, maxX,
minY, maxY
);
Такой подход полезен в GIS, логистике, CAD и любых сценариях, где нужно эффективно искать пересечения областей
Служебная диагностика
Виртуальные таблицы позволяют посмотреть на внутреннее устройство базы без внешних утилит. Это помогает при тюнинге, анализе страниц, проверке индексов и поиске причин роста файла
Как это работает внутри
Модуль виртуальной таблицы регистрируется через API SQLite, после чего движок вызывает методы вроде xCreate, xConnect, xBestIndex, xFilter, xNext и xRowid. Самый важный для производительности — xBestIndex: именно там модуль сообщает SQLite, какие условия фильтрации он умеет использовать эффективно
Если этот слой реализован небрежно, запросы будут работать, но оптимизатор не сможет протолкнуть предикаты вниз и производительность окажется слабой. Поэтому виртуальная таблица — это не только удобный интерфейс, но и ответственность за качество реализации
Когда использовать виртуальные таблицы в проекте
Подход хорош, если:
- нужен SQL-доступ к внешнему или нестандартному источнику данных
- важно оставить единый интерфейс запросов для приложения
- специализированная логика лучше живёт внутри расширения, чем в прикладном коде
Подход не лучший, если:
- нужна простая обычная таблица без особой логики
- критичны все стандартные DDL-возможности
- команда не готова поддерживать собственный extension-модуль
На практике виртуальные таблицы полезны там, где SQLite уже используется как embedded-движок, а рядом нужно подключить полнотекстовый поиск, диагностику или внешний формат данных без перехода на отдельную серверную систему
Частые ошибки
Первая ошибка — ожидать от виртуальной таблицы полного поведения обычной таблицы. Ограничения по индексам и ALTER TABLE лучше учитывать заранее
Вторая ошибка — не думать о планировщике. Если модуль плохо реализует выбор стратегии доступа, то даже хороший SQL поверх него будет медленным
Третья ошибка — использовать виртуальную таблицу там, где проще один раз загрузить данные в нормальную таблицу и работать уже с ней
Виртуальные таблицы — это механизм расширения SQLite, который позволяет превратить внешние данные или специализированные индексы в обычный SQL-интерфейс. Это один из самых сильных инструментов SQLite для сложных embedded-сценариев, если использовать его осознанно и понимать ограничения



