Опирается на правила:
PG-N-001…PG-N-094из PostgreSQL Style Guide → раздел Именование объектов.
Важно знать
snake_caseдля всего: таблицы, колонки, индексы, constraints, sequences, views.- Никаких кавычек в идентификаторах —
"OrderDoc"принуждает PG сохранить регистр.- Таблицы — существительные в единственном числе (
order_doc,customer).- ID-колонка —
id(неcustomer_idв таблицеcustomer).- Boolean с
is_/has_/can_префиксом.- Время —
_atдля timestamp, без — для дат; деньги —_amount/_price/_rate.- Soft-delete —
deleted_at timestamptz, неis_deleted boolean.- Индексы —
ix_/uk_/ck_/fk_префикс.- Зарезервированные слова (
user,order,group) — не использовать.
Именование — самая дешёвая и недооценённая дисциплина. Не даёт ускорения, но через два года команда читает чужой код легко, а DBA не путается в EXPLAIN.
Регистр и стиль
PG-N-001..002: snake_case, без кавычек.
-- ✓
CREATE TABLE order_doc (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
-- ✗
CREATE TABLE OrderDoc (
Id bigint,
customerId bigint,
"CreatedAt" timestamptz
);
"OrderDoc" с двойными кавычками → PG сохраняет регистр, требует кавычек в каждом запросе. order_doc без кавычек — case-insensitive.
Таблицы
PG-N-010..012:
- Существительные в единственном числе:
order_doc,customer,product,payment. Неorders/customers/products. - Junction-таблицы (M:N) — обе сущности:
order_item,customer_role,product_tag. - Префикс домена для крупных схем —
order_*,catalog_*или отдельная schema.
Спорная конвенция (Hibernate/JPA предпочитает plural). Главное — выбрать одно и не смешивать.
Колонки
PG-N-020..027:
| Что | Конвенция | Пример |
|---|---|---|
| ID | id (не customer_id в customer) | id bigint |
| FK | <parent>_id | customer_id, order_id |
| Boolean | is_/has_/can_ | is_active, has_avatar |
| Timestamp | _at суффикс | created_at, expires_at |
| Date | без суффикса или _on | born_on, holiday_date |
| Деньги | _amount/_price/_rate | total_amount, discount_rate |
| Длительность | явная единица | ttl_seconds, delivery_days, session_timeout_ms |
| Enum | без префикса | status, type, currency |
| Размер коллекции | _count | view_count, items_count |
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz,
born_on date,
ttl_seconds integer,
total_amount numeric(15,2),
is_active boolean NOT NULL DEFAULT true,
view_count integer NOT NULL DEFAULT 0
Audit-колонки
PG-N-030..031:
Стандартный набор:
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
created_by bigint REFERENCES customer(id),
updated_by bigint REFERENCES customer(id),
version bigint NOT NULL DEFAULT 0 -- optimistic locking
Soft-delete: deleted_at, не is_deleted
-- ✓ — есть момент удаления
deleted_at timestamptz -- NULL = существует
-- ✗ — момент удаления потерян
is_deleted boolean
Из deleted_at тривиально получить boolean: WHERE deleted_at IS NULL.
Индексы и constraints
PG-N-040..046: префикс по типу.
| Тип | Префикс | Пример |
|---|---|---|
| Обычный индекс | ix_ | ix_order_customer_id, ix_order_status_created_at |
| Уникальный | uk_ | uk_customer_email, uk_product_sku |
| Foreign key | fk_ | fk_order_item_order_id |
| Check | ck_ | ck_order_total_positive |
| Primary key | pk_ (обычно auto: <table>_pkey) | |
| Trigger | tr_ | tr_order_doc_audit |
CREATE INDEX ix_order_customer_id ON order_doc (customer_id);
CREATE INDEX ix_order_status_created_at ON order_doc (status, created_at);
CREATE UNIQUE INDEX uk_customer_email ON customer (email);
CREATE INDEX ix_account_email_lower ON account ((lower(email))); -- functional
CREATE INDEX ix_order_active ON order_doc (customer_id) WHERE status IN ('NEW','PAID'); -- partial
ALTER TABLE order_item
ADD CONSTRAINT fk_order_item_order_id
FOREIGN KEY (order_id) REFERENCES order_doc(id);
CONSTRAINT ck_order_total_positive CHECK (total_amount >= 0)
Явные имена FK/CHECK дают читаемые сообщения об ошибках: CHECK constraint violated: ck_order_total_positive — сразу понятно правило.
Зарезервированные слова
PG-N-050: не использовать.
user, order, group, type, position, value, name, default, desc, asc, start, end, class.
Альтернативы:
- «Пользователь» →
customer/account/person. - «Заказ» →
order_doc/purchase/shipment.
Иначе каждый запрос — SELECT * FROM "user" с кавычками.
Полный список: SELECT * FROM pg_get_keywords() WHERE catcode IN ('R', 'T').
Длина имени
PG-N-060..062:
- Лимит PG — 63 символа (
NAMEDATALEN - 1). Больше — молча обрежется, риск коллизии. - Целевая — до 30 символов.
- Сокращения едины по проекту (если
usr— вездеusr).
Sequences и serial
PG-N-070: GENERATED ALWAYS AS IDENTITY сам генерирует sequence с именем <table>_<column>_seq. Не вмешивайся.
nextval('customer_id_seq') напрямую — только при ручном импорте данных.
View и materialized view
PG-N-080: суффикс _v или префикс v_. MV — _mv.
CREATE VIEW customer_active_v AS ...;
CREATE MATERIALIZED VIEW order_stats_mv AS ...;
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
CamelCase или "CamelCase" имена | PG-N-001, PG-N-002 | snake_case без кавычек |
customer_id как PK в таблице customer | PG-N-020 | id |
is_deleted boolean (soft-delete) | PG-N-031 | deleted_at timestamptz |
delivery_time integer (без единицы) | PG-N-025 | delivery_days, delivery_seconds |
"user", "order" (зарезервированные) | PG-N-050 | customer, order_doc |
tbl_orders / t_orders префикс | PG-N-090 | без префикса (PG знает что это таблица) |
created_timestamp (datatype в имени) | PG-N-091 | created_at |
data jsonb для главных полей | PG-N-094 | attributes, payload, metadata, config |
| FK без явного имени | PG-N-045 | fk_<child>_<column> |
| CHECK без явного имени | PG-N-046 | ck_<table>_<rule> |
| Имя > 63 символов | PG-N-060 | сокращать до 30 |
Mix usr / user / account | PG-N-062 | единая конвенция |
Куда дальше
- PG → Именование — нормативные формулировки.
- Типы индексов — какой индекс выбрать.
- Composite-индексы и левый префикс — порядок колонок.
- Миграции без даунтайма — переименование колонки.
- Время и таймзоны —
_atсуффикс. - Антипаттерны типов — сводка.