Bạn dùng COALESCE để hiển thị dòng “Chưa có ghi chú”, nhưng một số bản ghi vẫn trống. Giá trị đầu tiên có thể là chuỗi rỗng thay vì NULL. COALESCE tìm giá trị không NULL; nó không tự quyết định chuỗi có nội dung hữu ích với người đọc hay không.
Bài cập nhật ngày 10/11/2026, chạy truy vấn bằng SQLite 3.54.0 và đối chiếu PostgreSQL 17. Không chạy PostgreSQL server, ORM hoặc migration dữ liệu. Ví dụ xử lý một trường ghi chú tùy chọn; chính sách bỏ dấu cách phải được xét riêng nếu dữ liệu là mật khẩu, mã hoặc nội dung cần giữ nguyên định dạng.
Xem bốn đầu vào cạnh nhau
WITH notes(id, note) AS (
VALUES (1, NULL), (2, ''), (3, ' '), (4, ' Giao buoi sang ')
)
SELECT id, quote(note) AS input, quote(COALESCE(note, 'Chua co')) AS fallback
FROM notes
ORDER BY id;
Trong SQLite, quote giúp nhìn thấy ranh giới giá trị khi console hiển thị khoảng trắng khó phân biệt. Dòng NULL được thay bằng Chua co; dòng chuỗi rỗng vẫn là chuỗi rỗng; dòng ba dấu cách giữ ba dấu cách; nội dung ở dòng cuối vẫn có khoảng trắng hai bên.
SQLite core functions mô tả COALESCE và NULLIF. Dấu ngoặc và tên hàm ở đây đều hợp lệ, nên chỉ kiểm tra “query chạy được” sẽ không phát hiện khác biệt giữa ý định hiển thị và định nghĩa của hàm.
Chuẩn hóa trước, chọn dự phòng sau
Nếu quy tắc sản phẩm cho phép bỏ dấu cách ở hai đầu và coi chuỗi hết nội dung sau bước ấy là chưa có, viết các bước theo đúng thứ tự:
WITH notes(id, note) AS (
VALUES (1, NULL), (2, ''), (3, ' '), (4, ' Giao buoi sang ')
)
SELECT id,
COALESCE(NULLIF(TRIM(note), ''), 'Chua co') AS display_note
FROM notes
ORDER BY id;
Ba dòng đầu trả Chua co; dòng cuối trả Giao buoi sang. Đọc từ trong ra ngoài: TRIM bỏ dấu cách ở biên; NULLIF đổi kết quả bằng chuỗi rỗng thành NULL; COALESCE mới chọn dòng dự phòng. Đây là một chính sách cụ thể, không phải yêu cầu mọi cột text đều phải được xử lý như vậy.
Ví dụ là SELECT nên không viết lại dữ liệu gốc. Nếu cần chuẩn hóa lúc ghi, hãy xác định rõ hợp đồng API, validate ở đâu và những consumer nào đang phân biệt chuỗi rỗng với NULL. Đổi cách hiển thị và đổi dữ liệu lưu trữ là hai phạm vi thay đổi khác nhau.
Đừng mặc định TRIM bỏ mọi loại whitespace
SELECT
length(trim(' ')) AS spaces_after_trim,
length(trim(char(9))) AS tab_after_trim,
length(trim(char(10))) AS newline_after_trim;
Trong SQLite đã kiểm tra, ba kết quả là 0, 1 và 1. TRIM với mặc định trong ví dụ xử lý dấu cách, không tự trở thành một bộ chuẩn hóa toàn bộ ký tự trắng Unicode. Một tab đứng riêng vẫn có thể đi qua biểu thức dự phòng ở phần trước.
Hãy thêm ca tab, newline và những ký tự trắng thực sự xuất hiện trong nguồn của bạn. Nếu nghiệp vụ muốn coi chúng là trống, định nghĩa tập ký tự và nơi xử lý rõ ràng. Tránh sao chép quy tắc trim giữa database và ngôn ngữ ứng dụng rồi mặc định chúng có cùng phạm vi.
Đối chiếu với PostgreSQL
PostgreSQL 17 conditional expressions mô tả COALESCE và NULLIF theo ý tưởng tương ứng. String functions ghi rõ ký tự mặc định cho các thao tác trim/btrim. Khi chuyển query, kiểm tra kiểu cột và quy tắc database đang dùng, thay vì lấy kết quả SQLite làm log thực thi PostgreSQL.
Các trường character có độ dài cố định hoặc dữ liệu do driver chuyển đổi có thể cần xem thêm quy tắc riêng. Bài không kiểm thử những kiểu ấy. Bộ dữ liệu minh họa chỉ dùng text hoặc NULL với các giá trị đã nêu.
Checklist cho trường văn bản tùy chọn
Viết bảng kỳ vọng cho NULL, chuỗi rỗng, dấu cách ở biên, nội dung hợp lệ và nội dung chứa ký tự trắng khác. Chốt xem khoảng trắng ở giữa cần giữ không. Chọn một giá trị dự phòng dễ phân biệt trong test; sau khi đúng nghĩa dữ liệu mới nối với lớp dịch ngôn ngữ của giao diện.
Nếu dùng dữ liệu này để tìm kiếm hoặc so khớp, kiểm tra thêm tác động của chính sách chuẩn hóa; một cách hiển thị gọn không tự tạo ra định danh đúng. Bắt đầu bằng câu hỏi “thế nào là chưa có nội dung?” sẽ giúp bạn đặt TRIM, NULLIF và COALESCE đúng vai trò, thay vì mong một hàm dự phòng giải quyết toàn bộ dữ liệu rỗng.

