CREATE TABLE định nghĩa một schema
Mọi dữ liệu có cấu trúc trong SQLite đều nằm trong bảng, và bảng nào cũng bắt đầu bằng một câu lệnh CREATE TABLE. Bạn đặt tên, liệt kê các cột và có thể gắn thêm ràng buộc (constraint). SQLite ghi schema vào tệp cơ sở dữ liệu và bảng sẵn sàng để dùng.
Ví dụ nhỏ nhất mà vẫn hữu ích:
Ba cột, một khóa chính, một quy tắc NOT NULL. SQLite tự điền id vì đó là khóa chính kiểu số nguyên, và để email là NULL ở hàng thứ hai vì không có gì yêu cầu khác đi. Đó là toàn bộ hình dạng: tên, cột, ràng buộc. Mọi thứ còn lại trong trang này đều là biến thể của nó.
Cú pháp, từng phần một
Một định nghĩa cột có dạng name TYPE constraint constraint .... Kiểu là tùy chọn trong SQLite cổ điển (xem thêm ở trang type affinity), nhưng nên luôn ghi kiểu: người đọc và các công cụ đều dựa vào nó.
Vài điều đáng chú ý:
- Các ràng buộc nối với nhau bằng dấu cách:
NOT NULL UNIQUEtrênskunghĩa là cả hai quy tắc đều áp dụng. DEFAULT 1trênin_stockcho phép câuINSERTbỏ qua cột đó.- SQLite dùng
INTEGERcho giá trị boolean, vì không có kiểuBOOLEANgốc.0là sai,1là đúng. - Dấu phẩy thừa sau cột cuối cùng là lỗi cú pháp. Ở điểm này SQL khắt khe hơn JavaScript.
IF NOT EXISTS: không bị lỗi khi chạy lại
Chạy CREATE TABLE trên một cơ sở dữ liệu đã có bảng đó, SQLite sẽ báo lỗi:
Error: table users already exists
Lần đầu thì không sao, đến lần thứ một trăm thì phiền. IF NOT EXISTS khiến câu lệnh không làm gì khi bảng đã có sẵn:
Câu CREATE TABLE thứ hai không làm gì cả: không lỗi, không thay đổi schema. Đây là dạng bạn cần trong code khởi động, script migration và bất cứ đâu mà cùng một câu SQL có thể chạy nhiều lần.
Tuy nhiên cần lưu ý: IF NOT EXISTS chỉ kiểm tra tên. Nếu đã có một bảng trùng tên nhưng khác cột, SQLite sẽ để nguyên nó. Nó sẽ không "sửa" hay "nâng cấp" schema giúp bạn. Đó là việc của migration.
Ràng buộc: quy tắc đi cùng schema
Ràng buộc là cách bạn đẩy việc kiểm tra dữ liệu vào chính cơ sở dữ liệu. Bốn loại bạn sẽ dùng liên tục:
PRIMARY KEY: định danh duy nhất cho một hàng. Xem thêm ở trang tài liệu về khóa chính.NOT NULL: cột bắt buộc phải có giá trị.DEFAULT value: được dùng khi câuINSERTbỏ qua cột. Có thể là một giá trị cố định hoặc một biểu thức nhưdatetime('now').CHECK (expr): phải cho kết quả đúng với mọi hàng.UNIQUE (col, col): ràng buộc cấp bảng, đảm bảo tổ hợp các cột là duy nhất.
Ràng buộc được kiểm tra ở mọi lệnh INSERT và UPDATE. Hàng nào vi phạm sẽ bị từ chối và câu lệnh thất bại. Bắt dữ liệu xấu ngay trong cơ sở dữ liệu rẻ hơn nhiều so với bắt nó sau khi đã lan ra khắp ứng dụng.
Khóa ngoại
Khóa ngoại (foreign key) nói rằng "cột này trỏ tới một hàng trong bảng khác." Nó giữ dữ liệu nhất quán: bạn không thể tham chiếu tới một người dùng không tồn tại, và (với tùy chọn phù hợp) xóa một người dùng có thể xóa dây chuyền các đơn hàng của họ.
Một điểm dễ vấp của SQLite nên nhớ kỹ: việc kiểm tra khóa ngoại mặc định bị tắt. Bạn phải chạy PRAGMA foreign_keys = ON cho mọi kết nối muốn kiểm tra ràng buộc. Hầu hết driver của ứng dụng hoặc tự làm việc này, hoặc có một tùy chọn cài đặt; nếu driver của bạn không có, hãy chạy pragma ngay sau khi kết nối.
ON DELETE CASCADE ở đây nghĩa là xóa một người dùng sẽ tự động xóa các bài viết của họ. Các tùy chọn khác gồm SET NULL, RESTRICT và mặc định là NO ACTION, tức từ chối thao tác xóa nếu còn hàng con.
CREATE TABLE AS SELECT
Đôi khi bạn muốn nhanh chóng sao chép kết quả của một truy vấn thành bảng mới: để chụp lại dữ liệu, để sao lưu, hoặc làm bảng nháp khi phân tích. CREATE TABLE ... AS SELECT làm đúng việc đó:
Bảng mới sao chép tên cột, kiểu (ở mức tốt nhất có thể) và dữ liệu. Những gì nó không sao chép cũng quan trọng không kém: không khóa chính, không NOT NULL, không chỉ mục, không khóa ngoại. Nó chỉ là một bản chụp phẳng. Hãy coi nó là điểm khởi đầu cho công việc tạm thời, không phải cách nhân bản một schema thật.
Nếu bạn muốn lấy cấu trúc mà không lấy dữ liệu, hãy thêm WHERE 0:
Bạn có một bảng trống với cùng hình dạng cột, rất tiện cho các bảng lưu trữ sẽ được điền sau.
Bảng tạm
Bảng TEMP chỉ tồn tại trong kết nối cơ sở dữ liệu hiện tại. Đóng kết nối là nó biến mất: không cần dọn dẹp, không để lại schema thừa:
Những trường hợp nên dùng: chứa tạm các hàng cho một truy vấn nhiều bước, giữ kết quả trung gian quá rối để viết bằng CTE, tách riêng dữ liệu của từng kết nối trong một phiên chạy lâu. CREATE TEMP TABLE và CREATE TEMPORARY TABLE có cùng ý nghĩa.
Bạn cũng có thể kết hợp với AS SELECT: CREATE TEMP TABLE snapshot AS SELECT ... là mẫu phổ biến để "đóng băng" một tập kết quả giữa chừng khi phân tích.
Đặt tên trong dấu ngoặc
Phần lớn thời gian, tên cột và tên bảng là định danh trần. Nếu buộc phải dùng một từ khóa dành riêng hoặc tên có dấu cách, hãy bọc nó trong dấu ngoặc kép (theo chuẩn SQL) hoặc dấu backtick (thói quen từ MySQL mà SQLite cũng chấp nhận):
Cách này chạy được, nhưng lần nào tham chiếu tới bảng cũng thêm phiền. Nên dùng tên đơn giản như orders, selection, user_id và bỏ hẳn dấu ngoặc.
Một ví dụ thực tế
Ghép các phần lại với nhau: một schema nhỏ cho ứng dụng quản lý công việc, có IF NOT EXISTS để chạy được ở mỗi lần khởi động:
Đó là một schema có thể đưa vào dùng thật: tạo bảng an toàn khi chạy lại, khóa ngoại được kiểm tra, một CHECK giữ cho done luôn hợp lệ, giá trị mặc định hợp lý và các mốc thời gian tự điền.
Tiếp theo: kiểu dữ liệu
CREATE TABLE cho phép bạn viết INTEGER, TEXT, REAL, nhưng SQLite nổi tiếng là dễ dãi trong cách lưu các giá trị đó. Trang tiếp theo nói về năm lớp lưu trữ mà SQLite thực sự dùng, và vì sao kiểu bạn viết không phải lúc nào cũng là kiểu bạn nhận được.
Câu hỏi thường gặp
Làm sao tạo bảng trong SQLite?
Dùng CREATE TABLE name (column1 TYPE, column2 TYPE, ...). Mỗi cột có một tên và một kiểu tùy chọn, và bạn có thể thêm các ràng buộc như PRIMARY KEY, NOT NULL hoặc DEFAULT. Câu lệnh chạy ngay lập tức và bảng được lưu lại trong tệp cơ sở dữ liệu.
IF NOT EXISTS trong CREATE TABLE có tác dụng gì?
CREATE TABLE IF NOT EXISTS name (...) chỉ tạo bảng nếu chưa có bảng nào trùng tên. Không có nó, chạy lại script trên một cơ sở dữ liệu đã có bảng sẽ gây lỗi table already exists. Đây là cách bảo vệ tiêu chuẩn cho script migration và code khởi động ứng dụng.
Có thể tạo bảng từ một câu SELECT trong SQLite không?
Có: CREATE TABLE new_name AS SELECT ... tạo một bảng mới từ tập kết quả của truy vấn. Bảng mới sao chép tên cột và dữ liệu, nhưng không sao chép ràng buộc, khóa chính hay chỉ mục từ bảng nguồn. Hãy dùng nó cho ảnh chụp dữ liệu và bảng nháp, đừng dùng thay cho một schema thật.
Bảng tạm khác bảng thường ở điểm nào?
CREATE TEMP TABLE (hoặc CREATE TEMPORARY TABLE) tạo một bảng chỉ tồn tại trong kết nối cơ sở dữ liệu hiện tại và biến mất khi đóng kết nối. Bảng thường được lưu trong tệp cơ sở dữ liệu. Bảng tạm hữu ích để chứa kết quả truy vấn trung gian mà không làm rối schema.