Опирается на правила: PG-N-001PG-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-deletedeleted_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:

ЧтоКонвенцияПример
IDid (не customer_id в customer)id bigint
FK<parent>_idcustomer_id, order_id
Booleanis_/has_/can_is_active, has_avatar
Timestamp_at суффиксcreated_at, expires_at
Dateбез суффикса или _onborn_on, holiday_date
Деньги_amount/_price/_ratetotal_amount, discount_rate
Длительностьявная единицаttl_seconds, delivery_days, session_timeout_ms
Enumбез префиксаstatus, type, currency
Размер коллекции_countview_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 keyfk_fk_order_item_order_id
Checkck_ck_order_total_positive
Primary keypk_ (обычно auto: <table>_pkey)
Triggertr_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-002snake_case без кавычек
customer_id как PK в таблице customerPG-N-020id
is_deleted boolean (soft-delete)PG-N-031deleted_at timestamptz
delivery_time integer (без единицы)PG-N-025delivery_days, delivery_seconds
"user", "order" (зарезервированные)PG-N-050customer, order_doc
tbl_orders / t_orders префиксPG-N-090без префикса (PG знает что это таблица)
created_timestamp (datatype в имени)PG-N-091created_at
data jsonb для главных полейPG-N-094attributes, payload, metadata, config
FK без явного имениPG-N-045fk_<child>_<column>
CHECK без явного имениPG-N-046ck_<table>_<rule>
Имя > 63 символовPG-N-060сокращать до 30
Mix usr / user / accountPG-N-062единая конвенция

Куда дальше

  • PG → Именование — нормативные формулировки.
  • Типы индексов — какой индекс выбрать.
  • Composite-индексы и левый префикс — порядок колонок.
  • Миграции без даунтайма — переименование колонки.
  • Время и таймзоны — _at суффикс.
  • Антипаттерны типов — сводка.