До десятой версии PostgreSQL партиционирование собирали вручную через наследование таблиц и триггеры на INSERT — рабочая, но хрупкая конструкция, которую легко сломать неосторожным DDL. Декларативное партиционирование сделало то же самое штатным синтаксисом: секции объявляются прямо при создании таблицы, а планировщик сам знает о структуре и использует её при построении плана запроса.

Синтаксис для диапазонов по дате — обычное дело для таблиц с логами или метриками:

CREATE TABLE measurement (
    city_id   int NOT NULL,
    logdate   date NOT NULL,
    peaktemp  int,
    unitsales int
) PARTITION BY RANGE (logdate);

CREATE TABLE measurement_y2026m10 PARTITION OF measurement
    FOR VALUES FROM ('2026-10-01') TO ('2026-11-01');

Для PARTITION BY LIST значения перечисляются явно, что удобно для партиционирования по региону или арендатору (tenant) в мультитенантной схеме:

CREATE TABLE cities_ab PARTITION OF cities
    FOR VALUES IN ('a', 'b');

Каждая секция — полноценная таблица на диске, поэтому старые месяцы или годы можно выводить из оборота без единого DELETE. DETACH PARTITION отвязывает секцию от родителя, оставляя её отдельной таблицей (под переименование и архивацию), а DROP TABLE на самой секции удаляет миллионы строк за миллисекунды, потому что физически это просто удаление файла, а не построчная чистка с записью в WAL:

ALTER TABLE measurement DETACH PARTITION measurement_y2006m02 CONCURRENTLY;
DROP TABLE measurement_y2006m02;

CONCURRENTLY у DETACH (с 14-й версии) берёт SHARE UPDATE EXCLUSIVE вместо ACCESS EXCLUSIVE — обычные запросы к родительской таблице не блокируются на время операции, что критично на таблице, которая не прекращает принимать новые вставки.

Главная причина так усложнять DDL — не организация данных, а partition pruning: при запросе с условием на logdate планировщик доказывает, какие секции заведомо не содержат подходящих строк, и исключает их из плана ещё до выполнения. На таблице с двумя-тремя годами ежедневных партиций это разница между сканированием одной секции за сегодня и полным проходом по всей истории. Включено по умолчанию (enable_partition_pruning = on), проверить эффект проще всего через EXPLAIN — в плане просто не будет упоминаний отброшенных секций. Отдельная деталь, о которую часто спотыкаются: PRIMARY KEY/UNIQUE на партиционированной таблице обязаны включать столбец партиционирования — обычный PRIMARY KEY (id) без logdate в списке колонок PostgreSQL просто не даст создать.

#postgresql #dba