SQLite ATTACH Database

The ATTACH DATABASE statement connects a second (or third, or more) SQLite database file to your current session. Once attached, you query tables from all connected databases in a single SQL statement — no copying, no imports, no temporary merges needed.

The Multi-Database Analogy

Without ATTACH:                       With ATTACH:
──────────────────────────────────    ──────────────────────────────────
You have two filing cabinets           You open both cabinets at once
in separate rooms.                     and work from the same desk.

To compare records, you must           You query both directly and
photocopy from one room and            combine results in a single
carry papers to the other.             SQL statement.

ATTACH Syntax

ATTACH DATABASE 'filename.db' AS alias;

-- filename : path to the SQLite file
-- alias    : name you use to reference this database in queries

DETACH DATABASE alias;   -- disconnect when done

Basic Example

-- Session starts with school.db open
$ sqlite3 school.db

-- Attach a second database
sqlite> ATTACH DATABASE 'library.db' AS lib;

-- Now you have two databases in one session:
sqlite> .databases
main: /home/user/school.db   r/w
lib:  /home/user/library.db  r/w

-- Query library tables using "lib." prefix
sqlite> SELECT * FROM lib.books LIMIT 3;

-- Query school tables (main database) without prefix
sqlite> SELECT * FROM students LIMIT 3;
-- OR explicitly:
sqlite> SELECT * FROM main.students LIMIT 3;

Referencing Tables Across Databases

-- Syntax: database_alias.table_name
SELECT * FROM main.students;   -- main database
SELECT * FROM lib.books;       -- attached database

-- You can also omit "main." for the primary database
SELECT * FROM students;        -- same as main.students

JOIN Across Two Databases

-- school.db has: students (id, name, book_id)
-- library.db has: books (id, title, author)

-- Find each student's borrowed book
SELECT s.name      AS student,
       b.title     AS book,
       b.author    AS written_by
FROM   main.students  s
JOIN   lib.books      b  ON s.book_id = b.id
ORDER  BY s.name;

student │ book                   │ written_by
────────┼────────────────────────┼────────────
Alice   │ Atomic Habits          │ James Clear
Bob     │ Deep Work              │ Cal Newport
Carol   │ Thinking Fast and Slow │ Kahneman

Copy Data Between Databases

-- Copy all books from lib into main database
ATTACH DATABASE 'library.db' AS lib;

INSERT INTO main.books
  SELECT * FROM lib.books;

-- Copy only specific rows
INSERT INTO main.archive_books
  SELECT * FROM lib.books WHERE year < 2000;

Attach Multiple Databases

-- Attach up to 10 databases (SQLite default limit)
ATTACH DATABASE 'sales_2022.db'  AS s2022;
ATTACH DATABASE 'sales_2023.db'  AS s2023;
ATTACH DATABASE 'sales_2024.db'  AS s2024;

-- Query all three years in one UNION
SELECT '2022' AS year, SUM(amount) AS revenue FROM s2022.orders
UNION ALL
SELECT '2023', SUM(amount) FROM s2023.orders
UNION ALL
SELECT '2024', SUM(amount) FROM s2024.orders;

year │ revenue
─────┼──────────
2022 │ 450000
2023 │ 620000
2024 │ 390000

ATTACH with In-Memory Database

-- Attach a temporary in-memory database for scratch work
ATTACH DATABASE ':memory:' AS temp_work;

CREATE TABLE temp_work.staging (
  id      INTEGER,
  data    TEXT
);

INSERT INTO temp_work.staging SELECT id, name FROM main.students;

-- Process in temp_work, then write results back to main
INSERT INTO main.processed
  SELECT id, UPPER(data) FROM temp_work.staging;

-- temp_work disappears when you DETACH or close the session
DETACH DATABASE temp_work;

Transactions Across Attached Databases

-- A BEGIN transaction covers ALL attached databases
BEGIN;

UPDATE main.inventory SET stock = stock - 5 WHERE product_id = 1;
INSERT INTO lib.shipment_log (product_id, qty, shipped_at)
  VALUES (1, 5, datetime('now'));

COMMIT;   -- both changes commit together
-- or ROLLBACK; to undo both

DETACH DATABASE

-- Remove an attached database from the session
DETACH DATABASE lib;

-- Verify it is gone
sqlite> .databases
main: /home/user/school.db   r/w
-- lib is no longer listed

-- Cannot detach "main" (the primary database)
DETACH DATABASE main;
-- Error: cannot detach database main

Checking Attached Databases

-- Shell command
sqlite> .databases

-- SQL equivalent
SELECT * FROM pragma_database_list;

seq │ name │ file
────┼──────┼────────────────────────────
0   │ main │ /home/user/school.db
2   │ lib  │ /home/user/library.db

Attach Limitations

LIMITATION                          NOTES
──────────────────────────────────────────────────────────────────
Max 10 databases per session        Configurable at compile time
Cannot attach same file twice       Each file gets one alias
Cannot detach main database         Only attached databases
ATTACH is session-scoped            Must re-attach every new session
Cross-DB views not persistent       Views stay in one database
Foreign keys do not cross databases FK must be in the same database

Practical Pattern: Yearly Archive Strategy

-- Keep current year in main.db, archive older years separately

-- 1. Move old data to archive
ATTACH DATABASE '2023_archive.db' AS arch2023;

INSERT INTO arch2023.orders
  SELECT * FROM main.orders WHERE strftime('%Y', order_date) = '2023';

DELETE FROM main.orders WHERE strftime('%Y', order_date) = '2023';

DETACH DATABASE arch2023;

-- 2. Later, when you need historical data:
ATTACH DATABASE '2023_archive.db' AS arch2023;

SELECT * FROM main.orders
UNION ALL
SELECT * FROM arch2023.orders
ORDER BY order_date DESC;

ATTACH in Python

import sqlite3

conn = sqlite3.connect("school.db")

# Attach the library database
conn.execute("ATTACH DATABASE 'library.db' AS lib")

# Query across both
rows = conn.execute("""
  SELECT s.name, b.title
  FROM   main.students s
  JOIN   lib.books     b ON s.book_id = b.id
""").fetchall()

for row in rows:
    print(row)

conn.execute("DETACH DATABASE lib")
conn.close()

Key Takeaways

  • ATTACH DATABASE 'file.db' AS alias adds a second database to the current session
  • Reference attached tables as alias.table_name; the main database uses main.table_name
  • JOIN, INSERT, and SELECT all work across attached databases in one SQL statement
  • A single BEGIN transaction covers all attached databases simultaneously
  • DETACH DATABASE alias disconnects the database; the session must re-attach it next time

Leave a Comment

Your email address will not be published. Required fields are marked *