aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

UNIQUE và NULL trong PostgreSQL: chốt quy tắc cho khóa tùy chọn

Suong Nguyen by Suong Nguyen
30/10/2026
in Backend
0
585
SHARES
3.3k
VIEWS
Summarize with ChatGPTShare to Facebook

Một mã tham chiếu được phép để trống nhưng phải duy nhất khi có giá trị. Bạn thêm UNIQUE rồi ngạc nhiên vì hai dòng có NULL vẫn được lưu. Với PostgreSQL mặc định, điều đó có thể đúng với thiết kế của constraint; UNIQUE và NOT NULL đang giải quyết hai yêu cầu khác nhau.

Bài đối chiếu PostgreSQL 17 ngày 30/10/2026. Fixture UNIQUE mặc định được chạy trên SQLite CLI 3.54.0 để quan sát các ca cơ bản. Cú pháp NULLS NOT DISTINCT được rà từ tài liệu PostgreSQL 17, chưa chạy trên PostgreSQL server; không dùng SQLite để khẳng định đã kiểm thử tính năng riêng này.

Viết yêu cầu bằng ba câu riêng

Với mã liên kết tới hệ thống ngoài, có thể có ba hợp đồng: chưa có mã thì cho phép nhiều dòng NULL; chưa có mã thì chỉ cho phép tối đa một dòng NULL; hoặc mọi dòng đều bắt buộc có mã. Cả ba đều hợp lý trong những mô hình khác nhau, nhưng không dùng chung một constraint.

Constraints của PostgreSQL 17 ghi rằng UNIQUE mặc định không coi hai NULL là trùng trong phép kiểm tra tính duy nhất. Đừng suy diễn từ đó rằng biểu thức SQL NULL = NULL trả true; ngữ nghĩa so sánh và quy tắc constraint cần được đọc đúng chỗ.

Fixture: cho phép nhiều dòng chưa có mã

Chạy phần tạo bảng trong database thử nghiệm riêng:

CREATE TABLE external_links_demo (
  id INTEGER PRIMARY KEY,
  external_ref TEXT UNIQUE
);

INSERT INTO external_links_demo VALUES
  (1, NULL),
  (2, NULL),
  (3, 'REF-A');

SELECT id, COALESCE(external_ref, '(missing)') AS display_ref
FROM external_links_demo
ORDER BY id;

Kết quả đã quan sát trên SQLite là 1|(missing), 2|(missing), 3|REF-A. COALESCE ở đây chỉ giúp terminal hiển thị NULL rõ hơn, không thay đổi dữ liệu được lưu hay cách constraint hoạt động.

Sau đó thử riêng câu lệnh sau, vì đây là ca cố ý gây lỗi:

INSERT INTO external_links_demo VALUES (4, 'REF-A');

SQLite báo lỗi UNIQUE và không thêm dòng đó. Hai NULL được phép không có nghĩa mọi giá trị trùng đều được phép. Tài liệu CREATE TABLE của SQLite xác nhận quy tắc NULL cho UNIQUE của engine này; các engine khác cần kiểm tra tài liệu riêng.

Khi NULL cũng chỉ được xuất hiện một lần

Với PostgreSQL 17, có thể khai báo chính sách rõ ràng trên một bảng khác:

CREATE TABLE single_missing_demo (
  id INTEGER PRIMARY KEY,
  external_ref TEXT,
  UNIQUE NULLS NOT DISTINCT (external_ref)
);

Theo hợp đồng của constraint, NULL đầu tiên có thể được lưu, NULL thứ hai vi phạm tính duy nhất. Hai giá trị non-NULL giống nhau cũng vi phạm. Đây là kết quả kỳ vọng theo tài liệu PostgreSQL, không phải log thực thi PostgreSQL trong bài.

Trước khi chọn chính sách này cho mã tham chiếu tùy chọn, hỏi lại mô hình dữ liệu: vì sao toàn bảng chỉ được có một đối tượng chưa nhận mã? Nếu nhiều bản ghi đang chờ đồng bộ cùng được phép thiếu mã, default UNIQUE thường diễn đạt yêu cầu đó trực tiếp hơn.

Khi mã là bắt buộc

CREATE TABLE required_ref_demo (
  id INTEGER PRIMARY KEY,
  external_ref TEXT NOT NULL UNIQUE
);

NOT NULL loại trường hợp thiếu mã; UNIQUE ngăn mã trùng. Bài đã thử thêm NULL vào schema này trên SQLite và nhận lỗi NOT NULL. Chỉ đổi sang NULLS NOT DISTINCT không thay thế được yêu cầu bắt buộc có giá trị, vì chính sách ấy vẫn cho phép một NULL.

Chuỗi rỗng '' cũng không phải NULL. Cột text NOT NULL vẫn có thể nhận chuỗi rỗng nếu không có quy tắc khác. Quyết định trim, chuẩn hóa hoặc từ chối mã rỗng cần thống nhất với API; đừng biến NULL thành chuỗi rỗng trong client chỉ để vượt một bước validation.

Checklist trước migration

Đếm bản ghi NULL, tìm nhóm non-NULL trùng và kiểm tra dữ liệu được chuẩn hóa ra sao. Nếu dùng constraint nhiều cột, thử cả hai dòng có cùng tenant nhưng một phần khóa là NULL; không lấy kết quả của một cột để suy đoán mọi khóa ghép.

Khi thêm ràng buộc vào bảng thật, chuẩn bị cách xử lý dữ liệu đang vi phạm và đánh giá tác động khóa theo kế hoạch migration của dự án. CREATE INDEX của PostgreSQL còn mô tả tùy chọn NULLS DISTINCT/NOT DISTINCT ở mức index. Bài không đưa một lệnh ALTER chạy thẳng trên production hay đo thời gian xây index.

Giữ constraint trong database làm nguồn bảo vệ cuối cùng. Một lượt “SELECT xem có mã chưa” trong API không ngăn hai request đồng thời cùng vượt qua kiểm tra. Khi database từ chối, API cần chuyển thành phản hồi nghiệp vụ thích hợp mà không bỏ qua lỗi.

Lỗi thường gặp và giới hạn

  • Nghĩ UNIQUE đồng nghĩa trường bắt buộc nhập.
  • Dùng một NULL làm sentinel cho mọi giá trị chưa có mà không xem chính sách duy nhất.
  • Kiểm thử trên SQLite rồi áp dụng nguyên cú pháp riêng PostgreSQL.
  • Đồng nhất NULL, chuỗi rỗng và chuỗi chỉ có khoảng trắng.

Chốt điều kiện thiếu giá trị trước, rồi mới chọn constraint. Một schema tốt phải thể hiện đúng những dòng nào được phép cùng tồn tại, kể cả khi dữ liệu chưa hoàn chỉnh.

Nguồn tham khảo

  • PostgreSQL 17: Constraints
  • PostgreSQL 17: CREATE INDEX
  • SQLite: CREATE TABLE

Tags: Data integrityPostgreSQLSQL
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL WHERE và HAVING: lọc đúng dữ liệu trước và sau khi gom nhóm

by Suong Nguyen
29/10/2026
0

Dùng ví dụ đơn hàng để phân biệt điều kiện trên từng dòng với điều kiện trên tổng của một nhóm.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL UNION và UNION ALL: đừng làm mất sự kiện khi hợp nhất dữ liệu

by Suong Nguyen
28/10/2026
0

Dùng fixture log web và mobile để thấy UNION loại trùng theo toàn bộ cột được chọn, còn UNION ALL giữ số lần xuất hiện.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL ROW_NUMBER: lấy nguyên bản ghi mới nhất của mỗi khách hàng

by Suong Nguyen
27/10/2026
0

Xếp hạng đơn hàng theo từng khách, xử lý thời điểm trùng nhau và phân biệt lọc trước với lọc sau window function.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

LEFT JOIN và COUNT: đừng đếm khách chưa có đơn thành một đơn hàng

by Suong Nguyen
26/10/2026
0

Phân biệt COUNT(*) và COUNT(cột) trên LEFT JOIN, đặt điều kiện ON đúng mục tiêu và kiểm tra các hàng không có dữ liệu khớp.

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native RefreshControl: giữ refreshing đúng vòng đời của lượt tải
  • Phỏng vấn JavaScript: await forEach không chờ các callback async
  • Android ACTION_VIEW: mở liên kết HTTPS và xử lý khi không có app nhận

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply