Năm lớp lưu trữ, không phải hàng tá kiểu
SQLite lưu mọi giá trị dưới một trong năm lớp lưu trữ (storage class):
NULL: không có giá trị.INTEGER: số nguyên có dấu, từ 1 đến 8 byte tùy độ lớn.REAL: số thực dấu phẩy động IEEE 8 byte.TEXT: một chuỗi, lưu theo bảng mã của cơ sở dữ liệu (thường là UTF-8).BLOB: các byte thô, lưu đúng y như bạn đưa vào.
Vậy thôi. Không có BOOLEAN riêng, không có DATETIME, không có VARCHAR, không có DECIMAL. Các cơ sở dữ liệu khác có hàng chục kiểu; SQLite có năm, và mọi thứ khác đều được xây trên năm lớp đó.
typeof() cho biết lớp lưu trữ thật của từng giá trị. Bạn sẽ thấy integer, real, text, blob. Bốn lớp đó, cộng thêm null, là tất cả những gì SQLite biết.
Kiểu dữ liệu là động
Đây là phần làm người chuyển từ Postgres hay MySQL sang bất ngờ. Trong SQLite (khi không dùng STRICT), kiểu bạn khai báo cho cột giống một lời gợi ý hơn là một cam kết. Kiểu thật nằm ở từng giá trị:
Cả hai hàng đều được chấp nhận. Cột id chứa số nguyên ở hàng này và chuỗi ở hàng kia; body chứa chuỗi và số nguyên. SQLite vui vẻ lưu giá trị thuộc bất kỳ lớp nào vào bất kỳ cột nào.
Đây là kiểu động (dynamic typing), và đó là một lựa chọn thiết kế có chủ đích. Nó giúp SQLite dễ dãi khi làm nguyên mẫu và viết script nhanh. Nhưng nó cũng có nghĩa là một lỗi gõ nhầm trong code ứng dụng có thể lặng lẽ lưu sai dạng dữ liệu suốt nhiều năm. Nếu sự đánh đổi đó khiến bạn lo, và với hầu hết schema trên production thì nên lo, bảng STRICT là câu trả lời. Chúng ta sẽ sớm nói tới chúng.
Type affinity trong một đoạn
Kiểu khai báo trên cột không bị bỏ qua; nó cho cột một affinity (xu hướng kiểu). Khi bạn chèn một giá trị, SQLite cố chuyển nó về affinity của cột nếu có cách chuyển gọn gàng. Một cột TEXT nhận số 42 sẽ giữ nó thành chuỗi '42'; một cột INTEGER nhận chuỗi '42' sẽ lưu nó thành số nguyên 42. Nếu việc chuyển đổi làm mất thông tin, kiểu ban đầu được giữ nguyên.
Hàng một: số nguyên 42 bị chuyển thành chuỗi '42', và chuỗi '100' bị chuyển thành số nguyên 100. Hàng hai: '3.5' không thể chuyển sang INTEGER mà không mất dữ liệu, nên nó vẫn là chuỗi. Affinity có trang riêng ở phần sau; giờ bạn chỉ cần biết rằng kiểu của cột vẫn ảnh hưởng tới cách lưu, dù nó không bắt buộc.
Boolean
Không có lớp lưu trữ BOOLEAN. SQLite lưu boolean dưới dạng số nguyên: 0 là sai, 1 là đúng:
Các từ khóa TRUE và FALSE được nhận diện (từ SQLite 3.23) và được chuyển thành 1 và 0. Khai báo BOOLEAN cho cột affinity dạng số nhưng không giới hạn nó ở 0/1: không có STRICT, bạn có thể chèn 'maybe' và SQLite sẽ không phản đối.
Ngày và giờ
Cũng không có DATETIME. Bạn chọn một trong ba cách mã hóa, và các hàm ngày giờ của SQLite hoạt động với cả ba:
TEXTtheo ISO-8601:'2026-04-23 14:30:00'.REALdưới dạng số ngày Julian.INTEGERdưới dạng số giây Unix epoch.
Chuỗi ISO-8601 là lựa chọn phổ biến nhất: nó sắp xếp đúng khi so sánh như chuỗi, con người đọc được, và mọi hàm có sẵn (date(), time(), datetime(), strftime(), julianday()) đều nhận nó. Mỗi cột chọn một cách mã hóa và giữ nguyên; trộn nhiều định dạng trong cùng một cột là kiểu sai lầm sẽ quay lại cắn bạn sáu tháng sau.
VARCHAR, CHAR và những cái tên quen thuộc khác
SQLite chấp nhận các tên kiểu bạn biết từ những cơ sở dữ liệu khác: VARCHAR(255), CHAR(10), NVARCHAR, DECIMAL(10,2), DOUBLE, FLOAT, INT, BIGINT, MEDIUMINT. Tất cả đều được phân tích bình thường. Chúng chỉ được quy về một trong năm lớp lưu trữ theo quy tắc affinity.
VARCHAR(255) không giới hạn 255 ký tự: SQLite bỏ qua độ dài. DECIMAL(10, 2) không lưu số thập phân có độ chính xác cố định: nó nhận affinity dạng số và được giữ dưới dạng INTEGER hoặc REAL. Những cái tên này tồn tại chỉ để schema sao chép từ cơ sở dữ liệu khác chạy được; chúng không mang theo các ràng buộc mà tên đó ngụ ý ở nơi khác.
Nếu cần tính toán thập phân chính xác cho tiền bạc, hãy lưu số xu dưới dạng INTEGER. Kiểu dấu phẩy động REAL sớm muộn cũng sinh sai số làm tròn ở chữ số thập phân thứ ba.
NULL là một lớp lưu trữ
NULL không chỉ là "không có giá trị": nó là một giá trị với lớp lưu trữ riêng, được typeof() trả về:
b được báo là null. Điều này quan trọng vì NULL không bằng bất cứ thứ gì, kể cả một NULL khác. b = NULL không bao giờ đúng; bạn phải viết b IS NULL. Chủ đề này được nói kỹ ở trang về toán tử và NULL sau này, nhưng nó bắt đầu từ đây, với lớp lưu trữ.
Lưu byte với BLOB
BLOB lưu nguyên văn các byte thô, hữu ích cho ảnh nhỏ, giá trị băm, dữ liệu đã mã hóa, bất cứ thứ gì không phải chuỗi hay số:
Cú pháp x'...' cho phép viết blob dạng hex trong SQL; từ code ứng dụng, bạn thường truyền một mảng byte qua tham số. length() trên một blob trả về số byte, không phải số ký tự.
Một lưu ý thực tế: SQLite sẵn lòng lưu blob lớn, nhưng kéo một blob 50 MB qua mọi truy vấn chạm vào hàng đó thì rất chậm. Với tệp lớn, hãy lưu tệp trên ổ đĩa và giữ đường dẫn trong cơ sở dữ liệu.
Những điều cần nhớ
- Năm lớp lưu trữ
NULL,INTEGER,REAL,TEXT,BLOBbao trọn mọi thứ. - Boolean là số nguyên; ngày tháng là chuỗi, số thực hoặc số nguyên (tùy bạn chọn).
- Kiểu khai báo của cột là gợi ý, không phải cam kết (trừ khi bạn dùng
STRICT). VARCHAR(255)và các tên tương tự được phân tích nhưng không ép độ dài hay độ chính xác mà chúng ngụ ý ở nơi khác.typeof(value)là người bạn đáng tin mỗi khi bạn không chắc thứ gì đang thật sự được lưu.
Tiếp theo: type affinity
Hành vi "gợi ý" mà chúng ta lướt qua có cả một bộ quy tắc chính xác phía sau: năm lớp affinity, suy ra từ tên kiểu khai báo, được áp dụng mỗi lần bạn chèn dữ liệu. Đó là trang tiếp theo, và nó là chìa khóa để đoán trước SQLite sẽ thực sự làm gì với các giá trị bạn đưa vào.
Câu hỏi thường gặp
SQLite hỗ trợ những kiểu dữ liệu nào?
SQLite có năm lớp lưu trữ: NULL, INTEGER, REAL, TEXT và BLOB. Mọi giá trị trong cơ sở dữ liệu đều được lưu dưới một trong năm lớp này. Những tên quen thuộc như VARCHAR(255), DATETIME hay BOOLEAN vẫn được chấp nhận trong CREATE TABLE để tương thích, nhưng khi lưu chúng quy về một trong năm lớp trên.
SQLite có kiểu boolean hoặc datetime không?
Không có lớp lưu trữ riêng cho chúng. Giá trị boolean được lưu dưới dạng INTEGER (0 và 1), dù SQLite cũng nhận các từ khóa TRUE và FALSE. Ngày giờ được lưu dưới dạng TEXT (chuỗi ISO-8601), REAL (số ngày Julian) hoặc INTEGER (số giây Unix epoch): bạn chọn cách mã hóa, và các hàm ngày giờ đều hoạt động với cả ba.
Vì sao kiểu dữ liệu trong SQLite được gọi là động?
Ở hầu hết cơ sở dữ liệu, một cột khai báo INTEGER sẽ từ chối chuỗi. Trong SQLite (mặc định), kiểu khai báo chỉ là một gợi ý: kiểu thật đi kèm từng giá trị, nên một cột TEXT có thể chứa số nguyên nếu bạn chèn vào. Sự linh hoạt này đôi khi có ích, đôi khi lại là cái bẫy. Bảng STRICT tắt cơ chế này.