Menu

SQLite ATTACH DATABASE: truy vấn chéo nhiều tệp

Cách ATTACH DATABASE cho phép bạn mở nhiều tệp SQLite trong cùng một kết nối, truy vấn chéo giữa chúng bằng tiền tố schema và tách ra khi đã dùng xong.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Một kết nối, nhiều tệp

Một kết nối SQLite không bị gắn chặt với một tệp duy nhất. Với ATTACH DATABASE, bạn có thể mở thêm các tệp .db khác bên cạnh tệp ban đầu, rồi truy vấn tất cả như thể chúng là các schema trong cùng một cơ sở dữ liệu. Đây là thứ gần nhất với "nhiều cơ sở dữ liệu trên một máy chủ" mà SQLite có.

Dạng cơ bản:

Tệp archive.db sẽ được tạo nếu chưa tồn tại, giống như cơ sở dữ liệu chính. Từ giờ trong phiên làm việc này, mọi thứ có tiền tố archive. đều nằm trong tệp thứ hai đó. Mọi thứ có tiền tố main. (hoặc không có tiền tố) nằm trong tệp gốc.

Kết nối của bạn luôn có sẵn hai schema ngầm định: main (tệp bạn mở đầu tiên) và temp (vùng nháp cho các bảng tạm). Việc gắn thêm sẽ bổ sung các schema khác.

Cú pháp và vai trò của alias

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

Alias là tên schema bạn dùng để ghi rõ bảng thuộc về đâu. Nó chỉ có hiệu lực trong kết nối hiện tại: một kết nối khác gắn cùng tệp đó có thể chọn alias khác. Hãy chọn tên ngắn gọn và dễ hiểu (archive, analytics, cache) vì bạn sẽ gõ nó rất nhiều.

Vài điều đáng biết:

  • Đường dẫn được tính tương đối so với thư mục làm việc của tiến trình, trừ khi là đường dẫn tuyệt đối.
  • Chuỗi ':memory:' gắn một cơ sở dữ liệu trong bộ nhớ mới dưới alias đó.
  • Alias không được trùng với main hoặc temp, và không được lặp lại giữa các lần gắn.

JOIN giữa các cơ sở dữ liệu

Đây là lý do chính khiến phần lớn mọi người dùng ATTACH. Khi hai tệp đã nằm trong cùng một kết nối, bạn có thể JOIN các bảng của chúng trong một truy vấn duy nhất:

Query planner xử lý cả hai schema giống hệt cách nó xử lý các bảng trong main. Chỉ mục trên các bảng được gắn vẫn được dùng. EXPLAIN QUERY PLAN hoạt động trên cả hai. Không có lượt đi về qua mạng nào: cả hai tệp đều được mở trong cùng một tiến trình.

Tính năng này thật sự hữu ích để tách dữ liệu nóng khỏi kho lưu trữ dữ liệu cũ, chia tệp riêng cho từng khách hàng (tenant), hoặc lấy dữ liệu tham chiếu từ một cơ sở dữ liệu tra cứu chỉ đọc.

Gắn ở chế độ chỉ đọc và trong bộ nhớ

Nếu cơ sở dữ liệu thứ hai là thứ bạn chỉ muốn đọc chứ không bao giờ sửa, chẳng hạn một bộ dữ liệu tham chiếu đi kèm ứng dụng, hãy gắn nó ở chế độ chỉ đọc bằng một URI:

Dạng URI yêu cầu thư viện SQLite bật SQLITE_OPEN_URI (CLI và phần lớn thư viện kết nối cho các ngôn ngữ đều bật sẵn). Khi đó mọi lệnh INSERT, UPDATE hay DELETE nhắm vào ref.* sẽ báo lỗi trước khi đụng đến tệp.

Gắn cơ sở dữ liệu trong bộ nhớ cũng tiện không kém để chuẩn bị dữ liệu trung gian:

scratch biến mất khi kết nối đóng lại. Nó giống temp nhưng bạn tự quyết định vòng đời của nó.

Transaction bao trùm mọi cơ sở dữ liệu đã gắn

Một cặp BEGIN/COMMIT duy nhất bao trùm các thao tác ghi vào main và mọi schema đã gắn. Hoặc tất cả cùng commit, hoặc tất cả cùng rollback: tính nguyên tử được giữ nguyên giữa các tệp:

Chuyển các hàng từ một bảng đang dùng sang tệp lưu trữ chính là loại thao tác cần đến sự đảm bảo này. Không có tính nguyên tử giữa các tệp, một sự cố giữa chừng sẽ để lại các hàng bị trùng, hoặc tệ hơn, các hàng bị mất.

Một lưu ý: khi có nhiều hơn một cơ sở dữ liệu đã gắn được ghi trong cùng một transaction, SQLite dùng một giao thức commit thận trọng hơn, cần đến một tệp journal tạm. Cách này chậm hơn commit trên một tệp, nhưng vẫn an toàn.

Tách cơ sở dữ liệu

Khi đã dùng xong một cơ sở dữ liệu được gắn, hãy bỏ nó ra:

DETACH DATABASE archive;

Tệp vẫn nằm nguyên trên đĩa: DETACH chỉ đóng handle trong kết nối hiện tại. Có hai hạn chế cần nhớ:

  • Bạn không thể tách main hay temp.
  • Bạn không thể tách một cơ sở dữ liệu đang ở trong một transaction hoặc còn câu lệnh đang mở trên nó.

Nếu quên tách thì cũng chẳng sao: đóng kết nối sẽ dọn dẹp mọi thứ.

Giới hạn và các lỗi thường gặp

Vài giới hạn thực tế đáng biết:

  • Mức trần mặc định là 10 cơ sở dữ liệu được gắn trên mỗi kết nối (cộng thêm main và temp). Mức tối đa lúc biên dịch là 125. Chạm giới hạn, bạn sẽ thấy too many attached databases - max 10.
  • Mỗi tệp được gắn dùng một bộ nhớ đệm trang (page cache) riêng. Gắn cả chục cơ sở dữ liệu lớn không hề miễn phí: RAM sẽ tăng lên.
  • Bản thân ATTACH không chạy được bên trong transaction. Hãy chạy nó trước BEGIN, hoặc sau COMMIT.

Vài lỗi bạn có thể sẽ gặp:

-- File doesn't exist and the directory isn't writable:
Error: unable to open database: 'missing/path.db'

-- You tried to write to a read-only attachment:
Error: attempt to write a readonly database

-- You used the same alias twice:
Error: database archive is already in use

Phần lớn các lỗi này đọc là hiểu ngay. Lỗi "already in use" hay khiến người ta vấp: ATTACH không thay thế alias đã có; bạn phải DETACH trước.

Một mô hình thực tế: tách dữ liệu nóng và lạnh

Gộp tất cả lại: một quy trình lưu trữ nhỏ chuyển các đơn hàng cũ hơn một năm ra khỏi cơ sở dữ liệu chính:

Các hàng cũ chuyển sang archive.orders, các hàng gần đây ở lại main. Báo cáo cần dữ liệu lịch sử có thể JOIN giữa cả hai; các truy vấn hằng ngày trên main.orders vẫn nhanh vì bảng nhỏ hơn. Cùng một kết nối, hai tệp, một transaction.

Tiếp theo: prepared statement

ATTACH là để cho một kết nối truy cập được nhiều dữ liệu hơn. Nhóm chủ đề tiếp theo nói về cách ứng dụng làm việc với SQLite an toàn và hiệu quả, bắt đầu từ prepared statement, nền tảng của việc gắn tham số và các truy vấn chống injection.

Câu hỏi thường gặp

ATTACH DATABASE làm gì trong SQLite?

ATTACH DATABASE 'file.db' AS alias mở thêm một tệp cơ sở dữ liệu SQLite thứ hai ngay trong kết nối hiện tại và đặt cho nó một tên schema. Sau đó, bạn có thể gọi các bảng của nó bằng alias.table_name và JOIN chúng với các bảng trong cơ sở dữ liệu chính trong cùng một truy vấn.

SQLite có thể gắn bao nhiêu cơ sở dữ liệu cùng lúc?

Mặc định, SQLite cho phép gắn tối đa 10 cơ sở dữ liệu trên mỗi kết nối, cộng thêm hai schema main và temp. Giới hạn cứng là 125, có thể cấu hình lúc biên dịch qua SQLITE_MAX_ATTACHED. Nếu chạm giới hạn, bạn sẽ nhận lỗi too many attached databases.

Có thể truy vấn chéo các cơ sở dữ liệu SQLite đã gắn trong một câu lệnh không?

Có. Sau khi gắn, hãy ghi rõ tên schema trước mỗi bảng: SELECT * FROM main.users JOIN archive.orders ON .... JOIN, truy vấn con và INSERT ... SELECT đều chạy được giữa các schema. Transaction cũng bao trùm mọi cơ sở dữ liệu đã gắn, nên một lệnh COMMIT có tính nguyên tử trên tất cả các tệp.

Làm sao tách một cơ sở dữ liệu SQLite?

Chạy DETACH DATABASE alias. Tệp vẫn nằm nguyên trên đĩa: DETACH chỉ đóng handle trong kết nối hiện tại. Bạn không thể tách main hay temp, và cũng không thể tách một cơ sở dữ liệu đang ở giữa một transaction.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu