Bỏ qua để đến nội dung
Knowledge Hub

Đặt tên trong PostgreSQL: snake_case, 63 byte và tên constraint Cơ bản

Thời gian đọc ước tính: 4 phút

Trang này trả lời: vì sao database nên dùng snake_case, PostgreSQL xử lý tên dài và tên trùng ra sao, và nên đặt tên constraint, index thế nào.

PostgreSQL gập identifier không quoted về chữ thường: Orders, ORDERS, orders là cùng một bảng. Còn "Orders" (có ngoặc kép) là tên khác và phân biệt hoa thường.

Đang tải diagram…

-- ORM tạo bảng bằng tên có quote
CREATE TABLE "OrderItems" ("orderId" bigint, "createdAt" timestamp);
SELECT orderId FROM OrderItems;
-- ERROR: relation "orderitems" does not exist
SELECT orderId FROM "OrderItems";
-- ERROR: column "orderid" does not exist
-- Từ giờ mọi câu SQL viết tay, dashboard, script đều phải quote từng tên.

Quy tắc đơn giản nhất (cũng là khuyến nghị của PostgreSQL wiki): chỉ dùng a-z, 0-9, _, không bao giờ cần quote. Map sang camelCase ở tầng ORM/query builder:

Công cụ Cách map
Prisma @map / @@map trong schema
TypeORM naming strategy (ví dụ snake_case strategy)
Kysely CamelCasePlugin
Knex hook wrapIdentifier / postProcessResponse

Identifier dài tối đa NAMEDATALEN - 1 byte, mặc định 63 byte. Tên dài hơn vẫn được chấp nhận nhưng bị cắt, PostgreSQL chỉ phát một NOTICE:

NOTICE: identifier "payment_provider_settlement_reconciliation_line_items_amount_cents_idx"
will be truncated to "payment_provider_settlement_reconciliation_line_items_amount_ce"

Migration tool thường nuốt NOTICE. Hậu quả thật (đã chạy trên PostgreSQL 17):

  • Hai tên khác nhau nhưng trùng 63 byte đầu sẽ đụng nhau: index thứ hai báo relation "..._amount_ce" already exists, migration fail giữa chừng.
  • Câu SQL dùng tên đầy đủ vẫn được cắt cùng cách nên vẫn chạy; nhưng công cụ so tên trong catalog với tên đầy đủ trong code (schema diff, kiểm tra drift) sẽ thấy lệch.

Giới hạn tính theo byte, không theo ký tự: đ chiếm 2 byte UTF-8, nên tên tiếng Việt có dấu ngắn hơn 63 ký tự nhiều. Thêm một lý do để chỉ dùng ASCII.

Quyết định Mặc định đề xuất Lý do
Bảng số ít hay số nhiều Chọn một, ví dụ số nhiều orders Không có đáp án đúng; trộn lẫn mới là lỗi
Primary key id Ngắn, ORM nào cũng hiểu
Foreign key <bảng số ít>_id: customer_id Đọc join là biết trỏ đi đâu
Thời điểm created_at timestamptz Hậu tố _at cho mốc thời gian; timestamptz lưu thời điểm tuyệt đối
Ngày không giờ birth_date date, due_on date Phân biệt với mốc thời gian
Boolean is_active, has_invoice Cùng quy tắc với code
Tiền amount_cents bigint hoặc numeric Đơn vị trong tên, không dùng float
Từ khoá SQL Tránh user, order, group làm tên bảng/cột Phải quote mỗi lần dùng

Với mốc thời gian thật, PostgreSQL wiki khuyên dùng timestamptz thay cho timestamp không time zone (trừ timestamp trừu tượng không làm phép tính).

Tên constraint và index: biết mặc định, rồi viết tường minh

Phần tiêu đề “Tên constraint và index: biết mặc định, rồi viết tường minh”

Không đặt tên thì PostgreSQL tự sinh theo mẫu <bảng>_<cột>_<hậu tố> (đọc từ source PG 17, đã chạy thử):

Đối tượng Hậu tố mặc định Ví dụ với bảng orders
Primary key pkey (không kèm cột) orders_pkey
Unique key orders_external_ref_key
Foreign key fkey orders_customer_id_fkey
Check (một cột) check orders_amount_cents_check
Exclusion excl orders_period_excl
Index thường idx orders_customer_id_idx
  • Trùng tên: PG thêm số (orders_customer_id_idx1).
  • Quá dài: PG cắt phần tên bảng/cột nhưng giữ nguyên hậu tố.

Mẫu mặc định này dễ đọc, nên dùng chính nó làm quy ước và viết tên ra trong migration. Migration DROP CONSTRAINT / DROP INDEX cần tên ổn định, không phụ thuộc PG đã cắt hay đánh số ra sao trên môi trường đó.

CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY,
customer_id bigint NOT NULL,
amount_cents bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT orders_pkey PRIMARY KEY (id),
CONSTRAINT orders_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES customers (id),
CONSTRAINT orders_amount_cents_check CHECK (amount_cents >= 0)
);
CREATE INDEX orders_customer_id_created_at_idx ON orders (customer_id, created_at);
Trước Vấn đề Sau
tblOrderInfo Tiền tố kiểu, Info rỗng nghĩa orders
"CreatedDate" timestamp Phải quote; “Date” nhưng có giờ; không có time zone created_at timestamptz
cust (cột FK) Viết tắt, không rõ trỏ đi đâu customer_id

Tiếp theo: API, event, env.