Chọn kiểu dữ liệu cũng giống như chọn công cụ để làm việc. Bạn đâu có dùng tua vít để đóng đinh đâu (hy vọng thế). Trong database cũng vậy: chọn đúng kiểu dữ liệu ảnh hưởng rất nhiều đến hiệu năng, tiết kiệm bộ nhớ và dễ quản lý dữ liệu. Ví dụ, nếu lưu tiền bằng REAL thì có thể gặp vấn đề về độ chính xác, còn dùng TEXT thay vì VARCHAR cho chuỗi ngắn sẽ làm database tốn nhiều bộ nhớ hơn.
Khi chọn kiểu dữ liệu, nhớ cân nhắc mấy yếu tố sau:
Tính chất dữ liệu
Xác định xem dữ liệu thuộc loại nào: số, chuỗi, giá trị logic, ngày tháng hay thứ gì phức tạp hơn như cấu trúc JSON chẳng hạn.
Dung lượng dữ liệu
Bạn định lưu bao nhiêu dữ liệu? Ví dụ, cho text tối đa 50 ký tự thì nên chọn VARCHAR(50) thay vì TEXT.
Độ chính xác và phạm vi
Có cần độ chính xác cao không (ví dụ cho tính toán tài chính)? Có muốn giới hạn phạm vi giá trị không?
Tần suất và kiểu truy vấn
Bạn sẽ truy cập dữ liệu thường xuyên không? Lưu ý là kiểu phức tạp như JSONB sẽ tốn nhiều tài nguyên xử lý hơn.
Ví dụ chọn kiểu dữ liệu
Dữ liệu khác nhau thì nhiệm vụ cũng khác nhau. Có chỗ cần chính xác từng đồng, có chỗ chỉ cần lưu cho đủ. Dưới đây là ví dụ chọn kiểu dữ liệu phù hợp cho từng tình huống cụ thể.
Dữ liệu tài chính
Khi lưu tiền, giống như kế toán, sai số là không chấp nhận được. Vì vậy, chọn tốt nhất là kiểu NUMERIC để đảm bảo độ chính xác cao. Ví dụ mình muốn tạo bảng với các cột như sau:
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| id | SERIAL | Khóa chính |
| amount | NUMERIC(10, 2) | Mười chữ số, trong đó hai số sau dấu phẩy |
| currency_code | CHAR(3) | Mã tiền tệ ISO, ví dụ "USD", "EUR" |
| transaction_date | TIMESTAMP | Thời gian giao dịch, mặc định là thời gian hiện tại |
Tại sao không dùng REAL? Vì số thực có thể bị mất chính xác, mà với tài chính thì chuyện này rất nguy hiểm.
Dữ liệu chuỗi
Nếu bạn lưu tên user, địa chỉ hay bất kỳ chuỗi nào, luôn đặt giới hạn độ dài tối đa nếu có thể. Ví dụ, VARCHAR(50) thay vì TEXT. Như vậy sẽ tránh lỗi và tối ưu bộ nhớ.
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| id | SERIAL | Khóa chính |
| username | VARCHAR(50) | Login của user, duy nhất và bắt buộc |
| VARCHAR(255) | ||
| bio | TEXT | Tiểu sử, cho phép text dài |
Nếu độ dài chuỗi cố định (ví dụ mã quốc gia hai ký tự), dùng CHAR:
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| code | CHAR(2) | Mã quốc gia ISO (ví dụ "US"), khóa chính |
| name | VARCHAR(100) | Tên quốc gia, bắt buộc nhập |
Dữ liệu thời gian
Để lưu thời gian sự kiện hoặc lịch trình, thường dùng TIMESTAMP vì nó chứa cả ngày và giờ.
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| id | SERIAL | Khóa chính |
| event_name | VARCHAR(100) | Tên sự kiện |
| start_time | TIMESTAMP | Thời gian bắt đầu sự kiện, bắt buộc |
| end_time | TIMESTAMP | Thời gian kết thúc sự kiện, bắt buộc |
Nếu chỉ cần giờ mà không cần ngày thì dùng TIME, còn chỉ cần ngày mà không cần giờ thì dùng DATE.
Định danh duy nhất
Khi cần tạo định danh mà đảm bảo duy nhất trên toàn cầu, hãy dùng UUID. Ví dụ, để tạo định danh giao dịch duy nhất:
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| request_id | UUID | Định danh duy nhất của request, mặc định tạo bằng gen_random_uuid() |
| endpoint | VARCHAR(255) | Địa chỉ API endpoint được gọi |
| timestamp | TIMESTAMP | Thời gian request, mặc định là thời gian hiện tại |
JSONB: cấu trúc phức tạp
Khi cần lưu dữ liệu mà cấu trúc có thể thay đổi hoặc phức tạp, ví dụ cài đặt user hoặc metadata, hãy dùng JSONB.
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| user_id | SERIAL | Khóa chính (ID user) |
| preferences | JSONB | Cài đặt user ở dạng JSON |
Dùng JSONB rất tiện, nhưng nhớ là nếu insert/update nhiều thì có thể làm giảm hiệu năng.
Mảng
Mảng phù hợp khi cần lưu danh sách dữ liệu cùng loại. Ví dụ, danh sách tag:
| Tên cột | Kiểu dữ liệu | Ghi chú |
|---|---|---|
| id | SERIAL | Khóa chính |
| title | VARCHAR(255) | Tiêu đề bài viết |
| tags | TEXT[] | Mảng tag |
Tổng quan kiểu dữ liệu theo nhiệm vụ
| Loại nhiệm vụ | Kiểu dữ liệu khuyên dùng | Ví dụ |
|---|---|---|
| Định danh bản ghi | SERIAL, BIGSERIAL, UUID |
id SERIAL PRIMARY KEY |
| Số lượng, số nguyên | INTEGER, BIGINT |
quantity INTEGER |
| Tính toán tài chính | NUMERIC |
price NUMERIC(10, 2) |
| Lưu trữ chuỗi | VARCHAR(n), TEXT |
username VARCHAR(50) |
| Chuỗi ngắn cố định | CHAR(n) |
status CHAR(1) |
| Ngày và giờ | DATE, TIME, TIMESTAMP |
created_at TIMESTAMP DEFAULT NOW() |
| Định danh duy nhất | UUID |
id UUID PRIMARY KEY DEFAULT gen_random_uuid() |
| Cấu trúc phức tạp (JSON) | JSONB |
metadata JSONB |
| Danh sách giá trị | ARRAY |
tags TEXT[] |
| Giá trị logic | BOOLEAN |
is_active BOOLEAN |
Chắc bạn đã quen với tất cả các kiểu trừ SERIAL. Thực ra SERIAL là INTEGER dùng làm định danh dòng trong bảng.
Nó tự động tăng thêm 1 mỗi khi thêm dòng mới vào bảng. Ví dụ thêm 10 dòng thì dòng đầu id là 1, dòng hai là 2, v.v. Chi tiết về cái này sẽ nói ở bài sau nhé :)
GO TO FULL VERSION