Menu

ATTACH DATABASE в SQLite: запросы по нескольким файлам

Как ATTACH DATABASE позволяет открыть несколько SQLite-файлов в одном соединении, обращаться к ним по префиксам схем и аккуратно отвязывать после работы.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Одно соединение — много файлов

Соединение SQLite не привязано к одному файлу. С помощью ATTACH DATABASE можно открыть рядом с основным ещё один или несколько .db-файлов и обращаться к ним так, словно это разные схемы внутри одной базы. Это самое близкое, что есть у SQLite, к «нескольким базам на одном сервере».

Базовый синтаксис:

Файл archive.db создастся, если его ещё нет — точно так же, как и основная база. С этого момента в текущем сеансе всё с префиксом archive. лежит во втором файле. Всё с main. (или без префикса) — в исходном.

В вашем соединении всегда есть две неявные схемы: main (файл, который вы открыли первым) и temp (черновое пространство для временных таблиц). ATTACH добавляет к ним свои.

Синтаксис и роль алиаса

ATTACH DATABASE 'path/to/file.db' AS alias_name;

Алиас — это имя схемы, по которому вы будете уточнять таблицы. Он живёт только внутри текущего соединения: другое соединение, открыв тот же файл, может назвать его иначе. Берите что-то короткое и по делу (archive, analytics, cache) — печатать его придётся часто.

Несколько важных моментов:

  • Путь относительный, если только не указан абсолютный — отсчитывается от рабочего каталога процесса.
  • Строка ':memory:' подключит свежую in-memory базу под этим алиасом.
  • Алиас не может конфликтовать с main или temp и не должен повторяться между подключениями.

JOIN между базами

Это та самая фишка, ради которой обычно и используют ATTACH. Как только два файла открыты в одном соединении, их таблицы можно джойнить в одном запросе:

Планировщик относится к обеим схемам так же, как к таблицам в main. Индексы на подключённых таблицах используются. EXPLAIN QUERY PLAN работает между ними. Никаких сетевых издержек — оба файла открыты в одном процессе.

Это правда удобно: вынести «горячие» данные от «холодных» архивов, разнести данные арендаторов по файлам или подтянуть справочные данные из read-only справочника.

Read-only и in-memory подключения

Если вторая база нужна только на чтение (например, прибитый к релизу справочник) — подключите её в режиме read-only через URI:

URI-форма требует, чтобы у библиотеки SQLite был включён SQLITE_OPEN_URI (он включён в CLI и в большинстве языковых биндингов). После этого любая попытка INSERT, UPDATE или DELETE против ref.* упадёт с ошибкой ещё до записи в файл.

In-memory подключения тоже отлично подходят для промежуточных вычислений:

scratch исчезает при закрытии соединения. Это как temp, только временем жизни управляете вы.

Транзакции охватывают все подключённые базы

Один BEGIN/COMMIT покрывает запись и в main, и в каждую подключённую схему. Либо коммитится всё, либо откатывается всё — атомарность работает между файлами:

Перенос строк из живой таблицы в архивный файл — ровно та операция, где такая гарантия как раз и нужна. Без атомарности по файлам падение посередине оставит вас либо с дубликатами, либо, что хуже, с потерянными строками.

Один нюанс: когда в транзакции пишется больше чем в одну подключённую базу, SQLite использует более осторожный протокол коммита с временным журналом. Это медленнее, чем коммит по одному файлу, но всё ещё безопасно.

Отключение

Когда подключённая база больше не нужна — отключите её:

DETACH DATABASE archive;

Файл остаётся на диске нетронутым — DETACH просто закрывает дескриптор в текущем соединении. Два ограничения, которые надо помнить:

  • main и temp отключить нельзя.
  • Нельзя отключить базу, которая сейчас в транзакции или у которой открыты statement-ы.

Забыли отключить — ничего страшного: при закрытии соединения всё подчистится само.

Лимиты и типичные ошибки

Полезные практические лимиты:

  • По умолчанию — 10 подключённых баз на соединение (плюс main и temp). Жёсткий максимум на этапе компиляции — 125. Упрётесь — получите too many attached databases - max 10.
  • Каждая подключённая база занимает страничный кэш. Подцепить десяток крупных баз не бесплатно — RAM растёт.
  • Сам ATTACH нельзя выполнять внутри транзакции. Делайте его до BEGIN или после COMMIT.

Несколько ошибок, на которые легко наткнуться:

-- Файла нет, и каталог недоступен на запись:
Error: unable to open database: 'missing/path.db'

-- Попытались писать в read-only подключение:
Error: attempt to write a readonly database

-- Использовали один и тот же алиас дважды:
Error: database archive is already in use

Большинство из них становятся очевидными, как только вы их прочитаете. Спотыкаются обычно на «already in use» — ATTACH не подменяет существующий алиас; сначала нужно сделать DETACH.

Реалистичный паттерн: разделение на горячие и холодные

Соберём всё вместе — небольшой архивный сценарий, который выносит из основной базы заказы старше года:

Старые строки уезжают в archive.orders, свежие остаются в main. Отчёты, которым нужна история, могут джойнить обе. Повседневные запросы к main.orders работают быстро, потому что таблица меньше. То же соединение, два файла, одна транзакция.

Дальше: подготовленные выражения

ATTACH — это про то, чтобы дать одному соединению доступ к большему объёму данных. Следующий блок тем — про то, как приложения общаются с SQLite безопасно и эффективно, и начинается он с подготовленных выражений (prepared statements) — фундамента для биндинга параметров и защиты от инъекций.

Часто задаваемые вопросы

Что делает ATTACH DATABASE в SQLite?

ATTACH DATABASE 'file.db' AS alias открывает второй файл базы внутри текущего соединения и даёт ему имя схемы. После этого к таблицам можно обращаться как alias.table_name и в одном запросе джойнить их с таблицами основной базы.

Сколько баз SQLite можно подключить одновременно?

По умолчанию SQLite разрешает до 10 подключённых баз на одно соединение, плюс схемы main и temp. Жёсткий потолок — 125, настраивается на этапе компиляции через SQLITE_MAX_ATTACHED. Упрётесь в лимит — получите ошибку too many attached databases.

Можно ли в одном запросе обратиться сразу к нескольким подключённым базам?

Да. После ATTACH уточняйте каждую таблицу её схемой — SELECT * FROM main.users JOIN archive.orders ON .... Между схемами работают и JOIN-ы, и подзапросы, и INSERT ... SELECT. Транзакции тоже охватывают все подключённые базы, так что COMMIT атомарен по нескольким файлам сразу.

Как отключить базу в SQLite?

Выполните DETACH DATABASE alias. Сам файл на диске остаётся целым — DETACH лишь закрывает дескриптор в текущем соединении. main и temp отключить нельзя, как и базу, которая в данный момент участвует в транзакции.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ