PostgreSQL Bài 6: Hệ thống kiểu dữ liệu cơ bản: Numeric, String, Boolean, Date/Time và Timezone handling
Việc lựa chọn kiểu dữ liệu (Data Type) trong thiết kế lược đồ không chỉ đơn thuần là để lưu trữ được thông tin, mà còn tác động trực tiếp đến:
-
Dung lượng đĩa và bộ nhớ cache: Càng tốn ít byte, càng chứa được nhiều dòng trong
shared_buffers. -
Hiệu năng truy vấn & Index: Các kiểu dữ liệu có kích thước cố định, đơn giản luôn được tính toán và so sánh nhanh hơn.
-
Tính chính xác của nghiệp vụ: Tránh các lỗi sai số làm tròn tài chính hoặc lệch múi giờ giao dịch xuyên biên giới.
Bài học này đi sâu vào 4 nhóm kiểu dữ liệu nền tảng nhất trong PostgreSQL cùng các quy tắc thực chiến không thể bỏ qua.
1. Nhóm số (Numeric Types): Số nguyên, Số thực và Bài toán Tiền tệ
PostgreSQL cung cấp hai trường phái lưu trữ số: Chính xác tuyệt đối (Exact/Arbitrary Precision) và Gần đúng (Inexact/Floating-point).
1.1. Họ số nguyên (Integers) Trong PostgreSQL / RDBMS Hiện Đại
| Kiểu dữ liệu | Dung lượng | Phạm vi giá trị | Ứng dụng thực tế |
|---|---|---|---|
| SMALLINT | 2 bytes | đến | Trạng thái đơn hàng, enum số, mã lỗi, tháng/năm ngắn hạn. |
| INTEGER (INT) | 4 bytes | tỷ đến tỷ | Khóa ngoại, số lượng tồn kho, lượt xem trang vừa phải. |
| BIGINT | 8 bytes | đến | Primary Key chuẩn, bảng lịch sử log, audit trail, transactions. |
Khóa tự tăng: Tránh
SERIAL, hãy dùng Identity Columns chuẩn SQL: Trước đây PostgreSQL dùngSERIAL/BIGSERIAL(tạo một sequence ngầm). Tuy nhiên, từ PostgreSQL 10+, hãy dùng cú pháp chuẩn ANSI SQL:SQL
-- Chuẩn khuyến nghị hiện đại: id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEYƯu điểm: Ngăn chặn việc vô tình
INSERTgiá trị thủ công đè lên sequence nếu không có mệnh đềOVERRIDING SYSTEM VALUE.
1.2. Số thực và Tiền tệ: Cạm bẫy giữa FLOAT và NUMERIC
SQL
-- Thử nghiệm tính toán dấu phẩy động (Floating point):
SELECT 0.1::FLOAT8 + 0.2::FLOAT8 = 0.3::FLOAT8 AS result;
-- Kết quả: FALSE (Do 0.1 + 0.2 trong chuẩn IEEE 754 là 0.30000000000000004)
-
REAL(4 bytes) /DOUBLE PRECISION(hoặcFLOAT8- 8 bytes):-
Lưu trữ theo chuẩn nhị phân IEEE 754.
-
Tốc độ tính toán trên CPU cực nhanh.
-
Nhược điểm: Có sai số làm tròn. Chỉ dùng cho tọa độ địa lý, thông số cảm biến IoT, dữ liệu đồ họa, tính toán khoa học.
-
-
NUMERIC(precision, scale)(hoặcDECIMAL):-
Lưu trữ chính xác tuyệt đối từng con số dưới dạng chuỗi số byte nén.
-
precision: Tổng số chữ số (cả trước và sau dấu phẩy). -
scale: Số chữ số sau dấu thập phân. -
Ví dụ:
NUMERIC(15, 2)lưu được tối đa999,999,999,999.99. -
Quy tắc bắt buộc: Luôn dùng
NUMERICcho tiền tệ, giá trị tài chính, lãi suất ngân hàng. Tuyệt đối không dùngFLOATđể lưu tiền.
-
2. Nhóm chuỗi ký tự (String Types): Sự thật về VARCHAR vs TEXT
Trong các hệ quản trị như MySQL hay Oracle, bạn thường phải cân nhắc kỹ việc đặt VARCHAR(50), VARCHAR(255) vì nó ảnh hưởng đến việc phân bổ bộ nhớ đệm hay cấu trúc row. Nhưng trong PostgreSQL thì khác.
2.1. Bản chất lưu trữ trong PostgreSQL
| Kiểu dữ liệu | Mô tả |
|---|---|
CHAR(n) |
Chuỗi có độ dài cố định. Nếu chuỗi ngắn hơn n, PostgreSQL sẽ tự động đệm thêm khoảng trắng (space-padded). Hầu như không nên dùng vì tốn dung lượng vô ích và dễ sinh lỗi so sánh chuỗi. |
VARCHAR(n) |
Chuỗi có độ dài tối đa là n. Ném lỗi nếu độ dài vượt quá giới hạn. |
VARCHAR |
Không giới hạn độ dài khai báo (hoạt động giống TEXT). |
TEXT |
Chuỗi có độ dài thay đổi, tối đa 1GB cho mỗi giá trị. |
Sự thật hiệu năng: Trong PostgreSQL engine,
VARCHAR(n),VARCHARvàTEXTsử dụng cùng một cấu trúc lưu trữ nội bộ (internal storage structure:varlena). Việc truy vấn hay đánh index trên cộtTEXTkhông hề chậm hơnVARCHAR(50). Giới hạn(n)trongVARCHAR(n)chỉ đóng vai trò là một lớp kiểm tra ràng buộc độ dài (Constraint check) trước khi ghi.Quy tắc thiết kế:
Nếu nghiệp vụ có giới hạn cứng logic rõ ràng (ví dụ: Mã bưu chính 10 ký tự, Mã định danh CCCD 12 số): Dùng
VARCHAR(n).Các trường nội dung mô tả, ghi chú, địa chỉ, bài viết: Dùng thẳng
TEXT.
3. Nhóm Logic (Boolean Type)
PostgreSQL hỗ trợ kiểu BOOLEAN (hoặc BOOL) với dung lượng lưu trữ 1 byte.
-
Trạng thái 3 giá trị (Three-valued logic): Một cột boolean có thể nhận 3 giá trị:
TRUE,FALSE, hoặcNULL(chưa xác định / Unknown). -
Hỗ trợ chuỗi linh hoạt khi nạp dữ liệu:
-
True:
'true','t','yes','y','1'. -
False:
'false','f','no','n','0'.
-
SQL
CREATE TABLE account_flags (
user_id BIGINT PRIMARY KEY,
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- Khuyến nghị thêm NOT NULL để tránh logic 3 giá trị phức tạp
is_verified BOOLEAN DEFAULT NULL
);
4. Nhóm Ngày tháng & Thời gian: Xử lý Timezone chuẩn xác
Đây là khu vực phát sinh nhiều lỗi nhất trong các hệ thống backend khi có người dùng truy cập từ nhiều múi giờ khác nhau.
4.1. Phân biệt TIMESTAMP vs TIMESTAMPTZ
PostgreSQL cung cấp 2 kiểu thời gian kèm ngày:
-
TIMESTAMP WITHOUT TIME ZONE(hay viết tắt làTIMESTAMP):-
Chiếm 8 bytes.
-
Lưu nguyên vẹn chuỗi thời gian được đưa vào mà không quan tâm múi giờ của client hay server.
-
Hiểm họa: Nếu server ở UTC lưu
2026-10-08 15:00:00, và một user ở Việt Nam (UTC+7) cũng đọc chuỗi đó thì hệ thống không biết 15:00 đó là giờ của ai.
-
-
TIMESTAMP WITH TIME ZONE(hay viết tắt làTIMESTAMPTZ):-
Chiếm 8 bytes.
-
Cơ chế lưu trữ: PostgreSQL luôn luôn chuyển đổi thời gian đầu vào về chuẩn UTC để lưu vật lý xuống ổ đĩa.
-
Cơ chế hiển thị: Khi client truy vấn, PostgreSQL lấy mốc giờ UTC từ đĩa và tự động chuyển đổi sang múi giờ hiện tại của phiên kết nối (
TimeZonesession setting).
-
SQL
-- Kiểm tra sự khác biệt thực tế:
SET timezone TO 'Asia/Ho_Chi_Minh'; -- UTC+7
CREATE TABLE event_logs (
ts_without TIMESTAMP,
ts_with TIMESTAMPTZ
);
INSERT INTO event_logs VALUES
('2026-10-08 12:00:00+00', '2026-10-08 12:00:00+00');
SELECT * FROM event_logs;
Kết quả trả về:
Plaintext
ts_without | ts_with
--------------------------+------------------------
2026-10-08 12:00:00 | 2026-10-08 19:00:00+07
ts_with đã được chuyển sang giờ Việt Nam (+7 tiếng) một cách chuẩn xác, trong khi ts_without vứt bỏ thông tin múi giờ.
Quy tắc vàng cho Backend: Luôn luôn sử dụng
TIMESTAMPTZcho các mốc thời gian diễn ra sự kiện (created_at,updated_at,order_date,login_time). Chỉ dùngTIMESTAMP WITHOUT TIME ZONEkhi đại diện cho một khái niệm thời gian trừu tượng không phụ thuộc địa lý (ví dụ: giờ mở cửa cố định của chuỗi cửa hàng: "8:00 AM mỗi ngày").
4.2. Kiểu khoảng thời gian: INTERVAL
Dùng để thực hiện các phép cộng trừ thời gian cực kỳ mạnh mẽ mà không cần viết logic phức tạp ở code ứng dụng:
SQL
-- Lấy tất cả user không hoạt động trong 30 ngày qua
SELECT * FROM users
WHERE last_login < CURRENT_TIMESTAMP - INTERVAL '30 days';
-- Cộng thêm 2 giờ 15 phút
SELECT CURRENT_TIMESTAMP + INTERVAL '2 hours 15 minutes';
5. Thực hành: Bảng đơn hàng hoàn chỉnh tối ưu kiểu dữ liệu
Áp dụng toàn bộ kiến thức vào một bảng thực tế:
SQL
CREATE TABLE orders.purchase_orders (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- 8 bytes, an toàn, không lo tràn số
order_code VARCHAR(32) NOT NULL UNIQUE, -- Giới hạn độ dài nghiệp vụ
buyer_id BIGINT NOT NULL, -- Foreign key chuẩn
subtotal NUMERIC(14, 2) NOT NULL, -- Không bao giờ dùng FLOAT cho tiền tệ
discount_percentage NUMERIC(5, 2) DEFAULT 0.00,
is_paid BOOLEAN NOT NULL DEFAULT FALSE, -- 1 byte
placed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, -- Luôn lưu mốc thời gian kèm múi giờ
estimated_delivery DATE NOT NULL, -- Chỉ cần ngày (4 bytes)
notes TEXT -- Độ dài động, không giới hạn giả tạo
);
6. Tóm tắt các quy tắc cốt lõi
-
Số nguyên: Dùng
BIGINT GENERATED ALWAYS AS IDENTITYcho Primary Key. DùngSMALLINTcho mã trạng thái/enum. -
Tiền tệ & Tài chính: Tuyệt đối dùng
NUMERIC(p, s). Bỏ quaFLOAT/DOUBLE PRECISIONđể tránh sai số làm tròn. -
Chuỗi:
TEXTvàVARCHAR(n)có hiệu năng tương đương nhau bên trong Postgres; chỉ dùngVARCHAR(n)khi cần kiểm tra giới hạn độ dài nghiệp vụ. Tránh dùngCHAR(n). -
Thời gian: Mặc định chọn
TIMESTAMPTZcho toàn bộ các trường thời gian sự kiện (created_at,updated_at) để dữ liệu luôn được quy về UTC chuẩn xác.
Bài 7 xem tiếp: Thiết kế bảng chuẩn hóa, Ràng buộc dữ liệu (Constraints) và Cascade Rules — bước sang Phần 2, chúng ta sẽ tìm hiểu cách thiết kế lược đồ quan hệ vững chắc với
CHECK,UNIQUE,FOREIGN KEY, cơ chếON DELETE CASCADEvsRESTRICT, và kỹ thuật kiểm soát toàn vẹn dữ liệu ở cấp độ database engine.
All rights reserved