Привет, Я DocuDroid!
Оценка ИИ поиска
Спасибо за оценку нашего ИИ поиска!
Мы будем признательны, если вы поделитесь своими впечатлениями, чтобы мы могли улучшить наш ИИ поиск для вас и других читателей.
GitHub

Партиционирование

Андрей Аксенов
Contents

Партиционирование (или секционирование) помогает эффективно работать с очень большими таблицами, например таблицами фактов, разбивая их на меньшие части — партиции (секции). Это позволяет ускорить выполнение запросов, поскольку оптимизатор Greengage DB обращается только к нужным партициям, а не ко всей таблице.

Партиционирование и распределение данных выполняют разные задачи в Greengage DB. Партиционирование делит большую таблицу на меньшие части для повышения производительности запросов и упрощения обслуживания, например, архивирования или удаления устаревших данных. Распределение, в свою очередь, определяет, как данные (и в партиционированных, и в непартиционированных таблицах) распределяются по сегментам для обеспечения параллельного выполнения запросов. Партиционирование не влияет на распределение данных по сегментам.

Объявление партиционирования включает метод партиционирования, описанный ниже, а также список столбцов или выражений, используемых в качестве ключа партиционирования.

Введение в партиционирование таблиц

Как работает партиционирование

Greengage DB поддерживает следующие типы партиционирования таблиц:

Партиционирование на основе диапазонов

Таблица разделяется на диапазоны значений одного или нескольких столбцов, используемых в качестве ключа партиционирования. Каждый диапазон охватывает свой набор значений без пересечений с другими диапазонами. Этот метод обычно применяется для дат или числовых идентификаторов.

Границы диапазонов:

  • включают нижнюю границу;

  • не включают верхнюю границу.

Например, для диапазонов 1 — 10 и 10 — 20 значение 10 относится ко второму диапазону.

Партиционирование на основе списков значений

Таблица разделяется путем явного задания набора значений, которые относятся к каждой партиции. Этот метод полезен для дискретных категорий, таких как регионы продаж или товарные линейки.

Хеш-партиционирование

Таблица разделяется с помощью хеш-функции, применяемой к ключу партиционирования.

Каждая партиция определяется значениями модуля и остатка от деления. Строка помещается в ту партицию, для которой выполняется условие:

хеш(ключ_партиционирования) % модуль = остаток

Этот метод обеспечивает равномерное распределение данных, когда отсутствует естественный принцип группировки.

Данный пример показывает таблицу sales с одноуровневым партиционированием, где каждая партиция охватывает один месяц в первом квартале 2025 года. Каждая партиция соответствует определенному диапазону дат, заданному правилами партиционирования:

sales
   ├─── sales_2025_01    (date >= '2025-01-01' AND date < '2025-02-01')
   ├─── sales_2025_02    (date >= '2025-02-01' AND date < '2025-03-01')
   └─── sales_2025_03    (date >= '2025-03-01' AND date < '2025-04-01')

В следующем примере показана таблица sales с двухуровневым партиционированием. Ежемесячные партиции дополнительно разбиваются на сабпартиции по столбцу region, разделяя данные на сабпартиции asia и europe:

sales
   ├─── sales_2025_01               (date >= '2025-01-01' AND date < '2025-02-01')
   │    ├─── sales_2025_01_asia          (region = 'Asia')
   │    └─── sales_2025_01_europe        (region = 'Europe')
   ├─── sales_2025_02               (date >= '2025-02-01' AND date < '2025-03-01')
   │    ├─── sales_2025_02_asia          (region = 'Asia')
   │    └─── sales_2025_02_europe        (region = 'Europe')
   └─── sales_2025_03               (date >= '2025-03-01' AND date < '2025-04-01')
        ├─── sales_2025_03_asia          (region = 'Asia')
        └─── sales_2025_03_europe        (region = 'Europe')

Партиционированная таблица является виртуальной таблицей и не хранит данные. Данные хранятся в ее партициях — обычных таблицах, связанных с партиционированной таблицей. Каждая партиция содержит подмножество строк, определяемое заданными границами. При вставке данных в партиционированную таблицу каждая строка автоматически направляется в соответствующую партицию на основе значений ключа партиционирования. Если значение ключа партиционирования строки изменяется и перестает соответствовать границам текущей партиции, строка автоматически перемещается в подходящую партицию.

Партиции в свою очередь могут быть определены как партиционированные таблицы, что позволяет строить многоуровневую схему партиционирования с сабпартициями. Все партиции должны иметь одинаковый с родительской таблицей набор столбцов. При этом каждая партиция может независимо определять собственные индексы, ограничения и значения по умолчанию, не влияя на другие партиции.

Невозможно напрямую преобразовать обычную таблицу в партиционированную или партиционированную таблицу в обычную. Однако существующую обычную или партиционированную таблицу можно присоединить к партиционированной таблице в качестве партиции, либо отсоединить партицию, преобразовав ее в самостоятельную таблицу. Такие операции часто используются для упрощения и ускорения задач управления и реорганизации данных.

Партициями также могут быть сторонние таблицы. В этом случае ответственность за соблюдение правил партиционирования и корректность данных в таких таблицах лежит на пользователе.

Когда использовать партиционирование

Используйте партиционирование, если к таблице применимы большинство из следующих пунктов:

  • Таблица представляет собой большую таблицу фактов.

    Таблицы фактов с миллионами строк хорошо подходят для партиционирования. Партиционирование небольших таблиц с тысячами строк и меньше, напротив, малоэффективно.

  • Вас не устраивает текущая скорость выполнения запросов к данным.

    Применяйте партиционирование только к тем таблицам, запросы к которым выполняются значительно медленнее, чем требуется.

  • Существует столбец, позволяющий разделить таблицу на примерно равные по размеру партиции.

    Выберите ключ для партиционирования, чтобы количество строк в каждой партиции было приблизительно одинаковым. Чем равномернее таблица разбита на небольшие части, тем выше будет прирост производительности за счет партиционирования. Например, разделение таблицы на 10 партиций одинакового размера может увеличить производительность запросов до 10 раз — при условии, что ключ партиционирования используется в предикатах запроса (см. следующий пункт).

  • Большинство запросов, требующих ускорения, используют ключ партиционирования в своих условиях.

    Партиционирование повышает производительность только тогда, когда оптимизатор может отфильтровать партиции на основе предикатов запроса. Если запрос сканирует все партиции, его выполнение может быть медленнее, чем при работе с непартиционированной таблицей. Убедитесь, что планы выполнения запросов содержат сканирование ограниченного числа партиций (partition elimination).

  • Существуют бизнес-требования по хранению исторических данных.

    Партиционирование хорошо подходит для управления хранением данных по периодам времени. Например, если нужно хранить данные только за последние 12 месяцев, можно удалить самую старую партицию и загрузить новые данные в новую партицию.

Избегайте создания большего числа партиций, чем необходимо. Чрезмерное партиционирование может негативно сказаться на работе системы, включая операции вакуумирования, восстановление сегментов, расширение кластера, проверку использования диска и другие операции.

Выбор синтаксиса партиционирования

Greengage DB 7 и более поздние версии сохраняют большую часть синтаксиса партиционирования из предыдущих версий — известного как классический синтаксис. Также добавлена поддержка декларативного синтаксиса партиционирования, заимствованного из PostgreSQL.

Классический синтаксис поддерживается для обеспечения обратной совместимости с более ранними версиями Greengage DB. Он предназначен для однородных партиционированных таблиц, в которых все партиции находятся на одном уровне и используют единое правило партиционирования. Если вы уже знакомы с партиционированием в Greengage DB 6 или используете таблицы, созданные с применением классического синтаксиса, вы можете продолжать его использовать.

В следующей таблице сравниваются ключевые возможности, чтобы помочь вам выбрать наиболее подходящий синтаксис для вашей модели данных.

Возможность Классический синтаксис Декларативный синтаксис

Гетерогенная иерархия партиций

Не поддерживается — все листовые партиции должны находиться на одном уровне

Поддерживается. Листовые партиции могут находиться на разных уровнях. Отдельные дочерние таблицы могут использовать разные столбцы партиционирования и разные стратегии партиционирования

Выражения в ключе партиционирования

Не поддерживаются

Поддерживаются

Многостолбцовое партиционирование по диапазону

Не поддерживается

Поддерживается

Многостолбцовое партиционирование на основе списков значений

Поддерживается (через составной тип)

Не поддерживается

Хеш-партиционирование

Не поддерживается

Поддерживается

Добавление партиции

Добавление партиции требует блокировки ACCESS EXCLUSIVE на родительскую таблицу

Присоединение партиции требует менее строгой блокировки SHARE UPDATE EXCLUSIVE на родительскую таблицу

Удаление партиции

Удаление партиции удаляет все данные, которые она содержит. Чтобы сохранить данные, сначала выполните обмен партиции со staging-таблицей

Партицию можно отсоединить напрямую, сохранив ее данные как отдельную таблицу

Шаблоны сабпартиций

Поддерживаются — определения дочерних таблиц по умолчанию согласованы с родительской

Не поддерживаются — требуется обеспечить согласованность определений таблиц самостоятельно

Обслуживание партиций

Операции выполняются через родительскую таблицу и требуют знания структуры иерархии партиций

Управление партициями выполняется напрямую, без необходимости учитывать их иерархию

Создание партиционированных таблиц

Для выполнения команд, описанных в следующих разделах, подключитесь к координатор-хосту Greengage DB с помощью psql, как описано в статье Подключение к Greengage DB с использованием psql. Затем создайте новую базу данных и подключитесь к ней:

CREATE DATABASE marketplace;
\c marketplace

Партиционирование задается на этапе создания таблицы с помощью команды CREATE TABLE. Чтобы выполнить партиционирование таблицы:

  1. Определите схему партиционирования: по временному диапазону, числовому диапазону, списку значений или хешу.

  2. Выберите столбец (или столбцы), который будет использоваться в качестве ключа партиционирования.

  3. Определите необходимое количество уровней партиционирования. Например, можно создать таблицу с партиционированием по месяцам, а затем разбить каждую месячную партицию на сабпартиции по региону продаж.

  4. Создайте партиционированную таблицу.

  5. Создайте партиции.

После создания партиции Greengage DB автоматически распределяет входящие данные по соответствующим партициям на основе их ограничений.

Сабпартиции могут использовать те же столбцы ключа партиционирования, что и родительская партиция. При этом границы каждой сабпартиции должны находиться в пределах границ соответствующей родительской партиции. Greengage DB не проверяет это автоматически.

Один и тот же столбец можно использовать повторно на разных уровнях партиционирования, например, для партиционирования по месяцам и последующего сабпартиционирования по дням.

При проектировании партиций рекомендуется использовать максимально детальный уровень, подходящий для ваших данных. Например, партиционирование по дням создает 365 партиций для года и часто является более практичным решением, чем многоуровневая схема год → месяц → день.

Более глубокая иерархия партиций может ускорить планирование запросов, тогда как более плоская структура обычно эффективнее на этапе выполнения.

Временные диапазоны

В следующем примере с помощью выражения PARTITION BY RANGE создается таблица с партиционированием по диапазону. В качестве ключа партиционирования используется столбец date:

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

Следующие команды определяют ежемесячные партиции за первый квартал 2025 года:

CREATE TABLE sales_2025_01
    PARTITION OF sales
        FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02
    PARTITION OF sales
        FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03
    PARTITION OF sales
        FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

В завершение создается партиция по умолчанию для хранения строк, выходящих за границы определенных диапазонов:

CREATE TABLE sales_other_dates
    PARTITION OF sales DEFAULT;

Числовые диапазоны

Данная команда создает партиционированную по диапазону таблицу sales на основе столбца id:

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (id);

Следующие команды определяют партиции, которые разделяют данные на диапазоны идентификаторов продаж:

CREATE TABLE sales_0_100000
    PARTITION OF sales
        FOR VALUES FROM (0) TO (100000);

CREATE TABLE sales_100000_200000
    PARTITION OF sales
        FOR VALUES FROM (100000) TO (200000);

CREATE TABLE sales_200000_300000
    PARTITION OF sales
        FOR VALUES FROM (200000) TO (300000);

Списки значений

Данный запрос создает таблицу sales с партиционированием по region на основе списка значений:

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    region TEXT,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY LIST (region);

Следующие команды определяют партиции для конкретных регионов:

CREATE TABLE sales_asia
    PARTITION OF sales
        FOR VALUES IN ('Asia');

CREATE TABLE sales_europe
    PARTITION OF sales
        FOR VALUES IN ('Europe');

Хеш-партиционирование

В следующем примере создается хеш-партиционированная таблица sales, в которой столбец id используется как ключ партиционирования:

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    region TEXT,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY HASH (id);

Далее создаются хеш-партиции с модулем 4, что обеспечивает равномерное распределение строк между четырьмя партициями на основе хеш-значения столбца id:

CREATE TABLE sales_prt_1
    PARTITION OF sales
        FOR VALUES WITH (MODULUS 4, REMAINDER 0);

CREATE TABLE sales_prt_2
    PARTITION OF sales
        FOR VALUES WITH (MODULUS 4, REMAINDER 1);

CREATE TABLE sales_prt_3
    PARTITION OF sales
        FOR VALUES WITH (MODULUS 4, REMAINDER 2);

CREATE TABLE sales_prt_4
    PARTITION OF sales
        FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Многоуровневое партиционирование

В следующем примере создается многоуровневая партиционированная таблица sales. На первом уровне используется RANGE-партиционирование по столбцу date, а каждая партиция по дате дополнительно разбивается с помощью LIST-партиционирования по столбцу region.

Базовая таблица определяет общую стратегию партиционирования:

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    region TEXT,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

Партиции первого уровня разбивают данные на месячные диапазоны для первого квартала 2025 года:

CREATE TABLE sales_2025_01
    PARTITION OF sales
        FOR VALUES FROM ('2025-01-01') TO ('2025-02-01')
    PARTITION BY LIST (region);

CREATE TABLE sales_2025_02
    PARTITION OF sales
        FOR VALUES FROM ('2025-02-01') TO ('2025-03-01')
    PARTITION BY LIST (region);

CREATE TABLE sales_2025_03
    PARTITION OF sales
        FOR VALUES FROM ('2025-03-01') TO ('2025-04-01')
    PARTITION BY LIST (region);

Далее каждая месячная партиция дополнительно делится по регионам:

  • Январь:

    CREATE TABLE sales_2025_01_asia
        PARTITION OF sales_2025_01
            FOR VALUES IN ('Asia');
    
    CREATE TABLE sales_2025_01_europe
        PARTITION OF sales_2025_01
            FOR VALUES IN ('Europe');
  • Февраль:

    CREATE TABLE sales_2025_02_asia
        PARTITION OF sales_2025_02
            FOR VALUES IN ('Asia');
    
    CREATE TABLE sales_2025_02_europe
        PARTITION OF sales_2025_02
            FOR VALUES IN ('Europe');
  • Март:

    CREATE TABLE sales_2025_03_asia
        PARTITION OF sales_2025_03
            FOR VALUES IN ('Asia');
    
    CREATE TABLE sales_2025_03_europe
        PARTITION OF sales_2025_03
            FOR VALUES IN ('Europe');

Загрузка данных в партиционированную таблицу

После создания структуры партиционированной таблицы родительская таблица не содержит данных. При вставке данных в родительскую таблицу Greengage DB направляет каждую строку в соответствующую листовую партицию в соответствии с правилами партиционирования. В многоуровневой иерархии данные хранятся только в партициях нижнего уровня (листовых).

Greengage DB отклоняет строки, которые не могут быть сопоставлены ни с одной листовой партицией, и возвращает ошибку при загрузке данных. Чтобы избежать отклонения таких строк, определите иерархию партиций с партицией по умолчанию. Все строки, не соответствующие ограничениям существующих партиций, будут сохраняться в партиции по умолчанию. Дополнительные сведения см. в разделе Добавление партиции по умолчанию.

Во время выполнения запроса оптимизатор сканирует иерархию партиций и использует ограничения партиций, чтобы определить, какие дочерние партиции необходимо проверить для выполнения условий запроса. Если существует партиция по умолчанию, она всегда включается в сканирование. В результате наличие данных в этой партиции может увеличивать общее время сканирования и снижать производительность запросов.

При использовании COPY или INSERT для загрузки данных в родительскую таблицу Greengage DB автоматически направляет каждую строку в соответствующую листовую партицию.

Распространенный подход к загрузке данных в партиционированные таблицы заключается в использовании промежуточной staging-таблицы: сначала данные загружаются в staging-таблицу, а затем она присоединяется к иерархии партиций. Дополнительные сведения см. в разделе Добавление новой партиции.

Отсечение партиций

Отсечение партиций — это метод оптимизации запросов, повышающий производительность при работе с партиционированными таблицами. В PostgreSQL-планировщике отсечение партиций можно включить или отключить с помощью параметра конфигурации сервера enable_partition_pruning.

Если отсечение партиций включено, планировщик анализирует выражение запроса WHERE и определения партиций, чтобы установить, может ли партиция содержать строки, удовлетворяющие условиям запроса. Если устанавливается, что партиция не удовлетворяет этим условиям, она исключается (отсекается) из плана выполнения, что сокращает объем сканируемых данных.

С помощью команды EXPLAIN и параметра конфигурации enable_partition_pruning можно проанализировать влияние отсечения партиций на планы выполнения запросов. В следующем примере показано различие между планами запросов при включенном и отключенном отсечении партиций на основе таблицы sales, созданной в разделе Многоуровневое партиционирование.

Сначала вставьте тестовые данные в таблицу sales:

INSERT INTO sales (id, date, region, amount)
SELECT gs.id,
       DATE '2025-01-01' + (gs.id % 90),
       CASE WHEN gs.id % 2 = 0 THEN 'Asia' ELSE 'Europe' END,
       round((random() * 1000)::NUMERIC, 2)
FROM generate_series(1, 40000) AS gs(id);

Следующий запрос выполняется с включенным отсечением партиций:

EXPLAIN (COSTS OFF)
SELECT count(*),
       sum(amount)
FROM sales
WHERE date >= DATE '2025-02-01'
  AND date < DATE '2025-02-03'
  AND region = 'Asia';

Если отсечение партиций включено, планировщик исключает нерелевантные партиции и сканирует только необходимые данные (Seq Scan on sales_2025_02_asia):

                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate
   ->  Gather Motion 4:1  (slice1; segments: 4)
         ->  Partial Aggregate
               ->  Seq Scan on sales_2025_02_asia
                     Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
 Optimizer: Postgres-based planner
(6 rows)

Далее для текущей сессии отключается отсечение партиций:

SET enable_partition_pruning = off;

Тот же запрос выполняется снова:

EXPLAIN (COSTS OFF)
SELECT count(*),
       sum(amount)
FROM sales
WHERE date >= DATE '2025-02-01'
  AND date < DATE '2025-02-03'
  AND region = 'Asia';

При отключенном отсечении партиций планировщик включает в план выполнения все партиции, включая те, которые не удовлетворяют условиям запроса:

                                                          QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate
   ->  Gather Motion 4:1  (slice1; segments: 4)
         ->  Partial Aggregate
               ->  Append
                     ->  Seq Scan on sales_2025_01_asia
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
                     ->  Seq Scan on sales_2025_01_europe
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
                     ->  Seq Scan on sales_2025_02_asia
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
                     ->  Seq Scan on sales_2025_02_europe
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
                     ->  Seq Scan on sales_2025_03_asia
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
                     ->  Seq Scan on sales_2025_03_europe
                           Filter: ((date >= '2025-02-01'::date) AND (date < '2025-02-03'::date) AND (region = 'Asia'::text))
 Optimizer: Postgres-based planner
(17 rows)

Отсечение партиций основано только на ограничениях, полученных из ключей партиционирования, а не на индексах. Поэтому наличие индексов по столбцам ключей партиционирования не требуется для работы отсечения.

Необходимость создания индекса для партиции зависит от характера запросов. Если запросы затрагивают лишь небольшую часть партиции, индекс может повысить производительность. Однако если запросы сканируют большую часть или всю партицию, индекс, как правило, не дает выигрыша и может привести к дополнительным накладным расходам.

Отсечение партиций может выполняться не только на этапе планирования запроса, но и во время его выполнения. Это полезно, когда значения, определяющие выбор партиций, неизвестны на этапе планирования. Например, это параметры, переданные в операторе PREPARE, значения, возвращаемые подзапросами, или значения параметров, используемые на внутренней стороне соединения с вложенным циклом. В таких случаях дополнительные партиции могут динамически исключаться по мере выполнения запроса.

Отсечение партиций во время выполнения запроса может выполняться на следующих этапах:

  • Во время инициализации плана запроса.

    На этом этапе отсечение партиций выполняется с использованием значений параметров, известных на момент начала выполнения плана. Партиции, исключенные на этапе инициализации, не отображаются в выводе EXPLAIN или EXPLAIN ANALYZE. Количество партиций, исключенных на этом этапе, можно увидеть в поле Subplans Removed в выводе EXPLAIN.

  • Во время выполнения запроса.

    Отсечение партиций также может выполняться динамически во время выполнения запроса с использованием значений, доступных только на этапе выполнения. Сюда входят значения, возвращаемые подзапросами, а также параметры, используемые при соединениях с вложенным циклом. Поскольку эти значения могут изменяться во время выполнения, отсечение партиций повторно пересчитывается при каждом изменении соответствующих параметров выполнения.

    Чтобы определить, были ли партиции исключены во время выполнения, проверьте поле loops в выводе EXPLAIN ANALYZE. Разные подпланы могут показывать разное число итераций в зависимости от того, как часто они выполнялись или исключались. Некоторые подпланы могут отображаться как (never executed), если они были полностью исключены во время выполнения.

Партиционирование существующей таблицы

Партиционирование таблицы можно указать только при ее создании. Если требуется партиционированная версия существующей таблицы, создайте новую партиционированную таблицу и загрузите в нее исходные данные.

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id);
INSERT INTO sales (id, date, amount)
SELECT gs.id,
       DATE '2025-01-01' + (gs.id % 90),
       round((random() * 1000)::NUMERIC, 2)
FROM generate_series(1, 40000) AS gs(id);
  1. Создайте новую партиционированную таблицу:

    CREATE TABLE sales_partitioned
    (
        LIKE sales
    )
        USING ao_row
        DISTRIBUTED BY (id)
        PARTITION BY RANGE (date);
    CREATE TABLE sales_2025_01 PARTITION OF sales_partitioned
        FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
    
    CREATE TABLE sales_2025_02 PARTITION OF sales_partitioned
        FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
    
    CREATE TABLE sales_2025_03 PARTITION OF sales_partitioned
        FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');
    
    CREATE TABLE other_dates PARTITION OF sales_partitioned DEFAULT;
  2. Загрузите данные из исходной таблицы в новую:

    INSERT INTO sales_partitioned
    SELECT *
    FROM sales;
  3. Удалите исходную таблицу:

    DROP TABLE sales;
  4. Переименуйте новую таблицу, присвоив ей имя исходной таблицы:

    ALTER TABLE sales_partitioned
        RENAME TO sales;
ПРИМЕЧАНИЕ

После создания новой таблицы необходимо заново выдать привилегии на таблицу. Узнайте больше в статье Роли и привилегии.

Просмотр информации о партиционировании

Ниже приведен пример партиционированной таблицы, используемой для демонстрации способов просмотра информации о партиционировании.

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    region TEXT,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01
    PARTITION OF sales
        FOR VALUES FROM ('2025-01-01') TO ('2025-02-01')
    PARTITION BY LIST (region);

CREATE TABLE sales_2025_02
    PARTITION OF sales
        FOR VALUES FROM ('2025-02-01') TO ('2025-03-01')
    PARTITION BY LIST (region);

CREATE TABLE sales_2025_03
    PARTITION OF sales
        FOR VALUES FROM ('2025-03-01') TO ('2025-04-01')
    PARTITION BY LIST (region);

CREATE TABLE sales_2025_01_asia
    PARTITION OF sales_2025_01
        FOR VALUES IN ('Asia');

CREATE TABLE sales_2025_01_europe
    PARTITION OF sales_2025_01
        FOR VALUES IN ('Europe');

CREATE TABLE sales_2025_02_asia
    PARTITION OF sales_2025_02
        FOR VALUES IN ('Asia');

CREATE TABLE sales_2025_02_europe
    PARTITION OF sales_2025_02
        FOR VALUES IN ('Europe');

CREATE TABLE sales_2025_03_asia
    PARTITION OF sales_2025_03
        FOR VALUES IN ('Asia');

CREATE TABLE sales_2025_03_europe
    PARTITION OF sales_2025_03
        FOR VALUES IN ('Europe');

Метакоманды psql

Метакоманда \d+ выводит информацию о таблице sales:

\d+ sales

Раздел Partitions в выводе содержит список дочерних таблиц, связанных с родительской таблицей:

                                Partitioned table "public.sales"
 Column |     Type      | Collation | Nullable | Default | Storage  | Stats target | Description
--------+---------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer       |           |          |         | plain    |              |
 date   | date          |           |          |         | plain    |              |
 region | text          |           |          |         | extended |              |
 amount | numeric(10,2) |           |          |         | main     |              |
Partition key: RANGE (date)
Partitions: sales_2025_01 FOR VALUES FROM ('2025-01-01') TO ('2025-02-01'), PARTITIONED,
            sales_2025_02 FOR VALUES FROM ('2025-02-01') TO ('2025-03-01'), PARTITIONED,
            sales_2025_03 FOR VALUES FROM ('2025-03-01') TO ('2025-04-01'), PARTITIONED
Distributed by: (id)
Access method: ao_row

Вы можете выполнить ту же команду для дочерней таблицы, чтобы увидеть ее дочерние таблицы, используемые для сабпартиционирования:

\d+ sales_2025_02

В дополнение к разделу Partitions в вывод также включается раздел Partition of, указывающий, что эта таблица наследует столбцы и ограничения таблицы sales:

                            Partitioned table "public.sales_2025_02"
 Column |     Type      | Collation | Nullable | Default | Storage  | Stats target | Description
--------+---------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer       |           |          |         | plain    |              |
 date   | date          |           |          |         | plain    |              |
 region | text          |           |          |         | extended |              |
 amount | numeric(10,2) |           |          |         | main     |              |
Partition of: sales FOR VALUES FROM ('2025-02-01') TO ('2025-03-01')
Partition constraint: ((date IS NOT NULL) AND (date >= '2025-02-01'::date) AND (date < '2025-03-01'::date))
Partition key: LIST (region)
Partitions: sales_2025_02_asia FOR VALUES IN ('Asia'),
            sales_2025_02_europe FOR VALUES IN ('Europe')
Distributed by: (id)
Access method: ao_row

Системные каталоги

Чтобы получить информацию о партиционированных таблицах в текущей базе данных, используйте один из описанных ниже способов.

pg_catalog.pg_partitioned_table

Чтобы вывести все партиционированные таблицы, выполните следующий запрос к таблице pg_partitioned_table:

SELECT pg_class.relname               AS partition_table_name,
       pg_attribute.attname           AS column_in_partition_key,
       class2.relname                 AS default_partition,
       pg_partitioned_table.partstrat AS partition_type
FROM pg_partitioned_table
         INNER JOIN pg_class ON pg_class.oid = pg_partitioned_table.partrelid
         INNER JOIN pg_attribute ON pg_attribute.attnum IN (SELECT unnest(pg_partitioned_table.partattrs)) AND
                                    pg_attribute.attrelid = pg_class.oid
         LEFT JOIN pg_class class2 ON class2.oid = pg_partitioned_table.partdefid
ORDER BY pg_class.relname;

Результат:

 partition_table_name | column_in_partition_key | default_partition | partition_type
----------------------+-------------------------+-------------------+----------------
 sales                | date                    |                   | r
 sales_2025_01        | region                  |                   | l
 sales_2025_02        | region                  |                   | l
 sales_2025_03        | region                  |                   | l
(4 rows)

Эта команда возвращает следующие данные:

  • partition_table_name — имя таблицы, используемое для обращения к партиции напрямую в DML-командах.

  • column_in_partition_key — имя столбца, используемого в качестве ключа партиционирования.

  • default_partition — название партиции, используемой по умолчанию (при ее наличии).

  • partition_type — тип партиционирования (r — на основе диапазонов, l — на основе списков значений).

Обратите внимание, что листовые партиции в результатах запроса не выводятся.

gp_toolkit.gp_partitions

Чтобы показать схему партиционирования указанной таблицы, выполните SQL-запрос к системному представлению gp_toolkit.gp_partitions:

SELECT partitiontablename,
       partitiontype,
       partitionlevel,
       partitionrank
FROM gp_toolkit.gp_partitions
WHERE tablename = 'sales'
ORDER BY partitionlevel,
         partitionrank;

Результат может выглядеть следующим образом:

  partitiontablename  | partitiontype | partitionlevel | partitionrank
----------------------+---------------+----------------+---------------
 sales_2025_01        | range         |              1 |             1
 sales_2025_02        | range         |              1 |             2
 sales_2025_03        | range         |              1 |             3
 sales_2025_01_asia   | list          |              2 |
 sales_2025_01_europe | list          |              2 |
 sales_2025_02_asia   | list          |              2 |
 sales_2025_02_europe | list          |              2 |
 sales_2025_03_asia   | list          |              2 |
 sales_2025_03_europe | list          |              2 |
(9 rows)

Вывод содержит следующие столбцы:

  • partitiontablename — имя таблицы, используемое для обращения к партиции напрямую в DML-командах.

  • partitiontype — тип партиционирования.

  • partitionlevel — уровень партиции в иерархии.

  • partitionrank — порядковый номер (rank) партиции в общем списке партиций на текущем уровне (начиная с 1). Заполняется только для range-партиций.

Вы также можете использовать столбец partitionboundary для получения полных спецификаций партиций.

Системные функции

Для получения информации о партиционированных таблицах можно использовать следующие функции:

pg_partition_tree(regclass)

Возвращает по одной строке для каждой таблицы или индекса в иерархии партиций указанной партиционированной таблицы или партиционированного индекса. Результат включает имя партиции, ее непосредственного родителя, флаг, указывающий, является ли она листовой партицией, и ее уровень в иерархии. Значение уровня начинается с 0 для отношения, переданного в качестве аргумента, 1 — для его непосредственных партиций, 2 — для партиций следующего уровня и далее по иерархии.

Возвращаемый тип: setof record

pg_partition_ancestors(regclass)

Возвращает все родительские отношения указанной партиции, включая саму партицию.

Возвращаемый тип: setof regclass

pg_partition_root(regclass)

Возвращает родительское отношение верхнего уровня иерархии партиционирования, к которой принадлежит указанное отношение.

Возвращаемый тип: regclass

Следующая команда отображает иерархию партиций для промежуточной таблицы sales_2025_02:

SELECT * FROM pg_partition_tree('sales_2025_02');

Результат:

        relid         |  parentrelid  | isleaf | level
----------------------+---------------+--------+-------
 sales_2025_02        | sales         | f      |     0
 sales_2025_02_asia   | sales_2025_02 | t      |     1
 sales_2025_02_europe | sales_2025_02 | t      |     1
(3 rows)

Обслуживание партиционированных таблиц

Управление партициями в основном выполняется с помощью следующих базовых операций:

  • CREATE TABLE …​ PARTITION OF — создание новой партиции и ее присоединение к партиционированной таблице.

  • ALTER TABLE …​ ATTACH PARTITION — присоединение существующей таблицы в качестве партиции.

  • ALTER TABLE …​ DETACH PARTITION — отсоединение партиции от партиционированной таблицы.

  • ALTER INDEX …​ ATTACH PARTITION — присоединение индекса к партиционированной таблице.

Добавление новой партиции

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01 PARTITION OF sales
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02 PARTITION OF sales
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03 PARTITION OF sales
    FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

Новые партиции создаются для данных, которые не соответствуют существующим границам партиционирования, например для нового периода времени или нового региона продаж. Партицию можно добавить как при создании партиционированной таблицы, так и после того, как таблица уже создана.

Создание и одновременное присоединение партиции

Следующий пример создает новую партицию непосредственно в определении партиционированной таблицы с помощью CREATE TABLE …​ PARTITION OF:

CREATE TABLE sales_2025_04 PARTITION OF sales
    FOR VALUES FROM ('2025-04-01') TO ('2025-05-01');

Создание таблицы с последующим присоединением в качестве партиции

В некоторых случаях предпочтительнее создать таблицу вне иерархии партиций, загрузить или проверить данные, а затем присоединить ее как партицию. Такой подход позволяет подготовить данные до их включения в партиционированную таблицу.

В следующем примере создается отдельная таблица со структурой родительской таблицы:

CREATE TABLE sales_2025_04
(
    LIKE sales
)
    USING ao_row;

Перед присоединением таблицы в качестве партиции можно добавить ограничение-проверку CHECK, соответствующее диапазону целевой партиции. Это позволяет избежать полного сканирования таблицы при проверке:

ALTER TABLE sales_2025_04
    ADD CONSTRAINT sales_2025_04_chk
        CHECK (
            (date IS NOT NULL)
                AND (date >= DATE '2025-04-01')
                AND (date < DATE '2025-05-01')
            );

На этом этапе можно загрузить данные или выполнить дополнительные шаги подготовки перед присоединением таблицы к иерархии партиций.

Когда таблица готова, ее можно присоединить к партиционированной таблице:

ALTER TABLE sales
    ATTACH PARTITION sales_2025_04
        FOR VALUES FROM ('2025-04-01') TO ('2025-05-01');

Следующий запрос проверяет, что новая партиция добавлена:

SELECT partitiontablename,
       partitionrangestart,
       partitionrangeend
FROM gp_toolkit.gp_partitions
WHERE tablename = 'sales';

Ожидаемый результат:

 partitiontablename | partitionrangestart | partitionrangeend
--------------------+---------------------+-------------------
 sales_2025_01      | '2025-01-01'        | '2025-02-01'
 sales_2025_02      | '2025-02-01'        | '2025-03-01'
 sales_2025_03      | '2025-03-01'        | '2025-04-01'
 sales_2025_04      | '2025-04-01'        | '2025-05-01'
(4 rows)

Команда ATTACH PARTITION требует блокировки уровня SHARE UPDATE EXCLUSIVE для партиционированной таблицы.

Перед выполнением команды рекомендуется определить CHECK-ограничение для присоединяемой таблицы, соответствующее границам целевой партиции. Если такое ограничение существует, можно избежать полного сканирования таблицы при проверке. Без этого ограничения Greengage DB выполняет полное сканирование таблицы, чтобы убедиться, что все строки соответствуют границам партиции, удерживая блокировку уровня ACCESS EXCLUSIVE на присоединяемой таблице. После успешного присоединения CHECK-ограничение можно удалить, так как оно больше не требуется. Если присоединяемая таблица сама является партиционированной, Greengage DB рекурсивно проверяет ее сабпартиции до достижения листовых партиций или до нахождения подходящего CHECK-ограничения.

Если партиционированная таблица содержит партицию по умолчанию, рекомендуется также определить CHECK-ограничение, исключающее диапазон присоединяемой партиции. Без этого ограничения Greengage DB выполняет сканирование партиции по умолчанию, чтобы убедиться, что в ней нет строк, которые должны относиться к новой партиции. Эта операция выполняется с удержанием блокировки ACCESS EXCLUSIVE на партиции по умолчанию. Если партиция по умолчанию сама является партиционированной, Greengage DB рекурсивно применяет ту же логику проверки к ее сабпартициям по тем же правилам, что описаны выше.

Добавление партиции по умолчанию

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01 PARTITION OF sales
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02 PARTITION OF sales
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03 PARTITION OF sales
    FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

Ключевое слово DEFAULT обозначает партицию по умолчанию. Если данные не попадают ни в одну из существующих партиций, Greengage DB направляет их в партицию по умолчанию.

Если партиция по умолчанию не определена и входящие данные не соответствуют ни одному ограничению партиций, Greengage DB отклоняет данные и возвращает ошибку. Добавление партиции по умолчанию гарантирует, что такие строки будут приняты и сохранены в таблице.

Партиционированная таблица может иметь только одну партицию по умолчанию. Партицию по умолчанию можно создать как при создании таблицы, так и позже.

Создание и одновременное присоединение партиции по умолчанию

Назначить партицию партицией по умолчанию при создании:

CREATE TABLE sales_other_dates
    PARTITION OF sales DEFAULT;

Создание таблицы с последующим присоединением в качестве партиции по умолчанию

Назначить существующую таблицу партицией по умолчанию:

CREATE TABLE sales_other_dates
(
    LIKE sales
)
    USING ao_row;

ALTER TABLE sales
    ATTACH PARTITION sales_other_dates DEFAULT;

Следующий запрос проверяет, что партиция по умолчанию была добавлена:

SELECT partitiontablename,
       partitionisdefault,
       partitionrangestart,
       partitionrangeend
FROM gp_toolkit.gp_partitions
WHERE tablename = 'sales';

Ожидаемый результат:

 partitiontablename | partitionisdefault | partitionrangestart | partitionrangeend
--------------------+--------------------+---------------------+-------------------
 sales_2025_01      | f                  | '2025-01-01'        | '2025-02-01'
 sales_2025_02      | f                  | '2025-02-01'        | '2025-03-01'
 sales_2025_03      | f                  | '2025-03-01'        | '2025-04-01'
 sales_other_dates  | t                  |                     |
(4 rows)

Индексирование партиционированных таблиц

CREATE TABLE sales
(
    id        INT,
    date      DATE,
    amount    DECIMAL(10, 2),
    category  TEXT NOT NULL
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01 PARTITION OF sales
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02 PARTITION OF sales
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03 PARTITION OF sales
    FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

INSERT INTO sales (id, date, amount, category)
SELECT gs.id,
       DATE '2025-01-01' + (gs.id % 90),
       round((random() * 1000)::NUMERIC, 2),
       categories[(random() * 9)::int + 1]
FROM generate_series(1, 100000) AS gs(id),
     LATERAL (VALUES (ARRAY[
         'Retail',
         'Online',
         'Wholesale',
         'Subscription',
         'Enterprise',
         'Gov',
         'Finance',
         'Health',
         'Education',
         'Energy'
         ])) AS c(categories);

При создании индекса по ключевым столбцам партиционированной таблицы автоматически создаются соответствующие индексы для всех существующих партиций. Все партиции, созданные или присоединенные позже, также будут иметь соответствующие индексы.

Следующий пример создает индекс для партиционированной таблицы sales:

CREATE INDEX sales_category_idx
    ON sales USING bitmap (category);

Greengage DB автоматически создает соответствующие индексы для каждой партиции. Чтобы проверить, что индексы были созданы как для родительской таблицы, так и для ее партиций, используйте метакоманду \di:

\di sales*

Вывод показывает партиционированный индекс, определенный для родительской таблицы, а также отдельные индексы для каждой партиции:

                                 List of relations
 Schema |            Name            |       Type        |  Owner  |     Table
--------+----------------------------+-------------------+---------+---------------
 public | sales_2025_01_category_idx | index             | gpadmin | sales_2025_01
 public | sales_2025_02_category_idx | index             | gpadmin | sales_2025_02
 public | sales_2025_03_category_idx | index             | gpadmin | sales_2025_03
 public | sales_category_idx         | partitioned index | gpadmin | sales
(4 rows)

Индекс или ограничение уникальности, определенные для партиционированной таблицы, являются виртуальными, как и сама таблица. Фактические данные индекса хранятся в соответствующих индексах отдельных партиций.

Создание индексов на уровне партиционированной таблицы удобно, поскольку обеспечивает единообразное индексирование всех существующих и будущих партиций. Чтобы снизить конкуренцию за блокировки при создании индексов, можно использовать CREATE INDEX …​ ON ONLY для партиционированной таблицы:

CREATE INDEX sales_category_idx
    ON ONLY sales USING bitmap (category);

Такой индекс изначально помечается как недопустимый, и Greengage DB не создает автоматически соответствующие индексы для партиций. Индексы для каждой партиции затем можно создать отдельно:

CREATE INDEX sales_2025_01_categories_idx
    ON sales_2025_01 USING bitmap (category);
CREATE INDEX sales_2025_02_categories_idx
    ON sales_2025_02 USING bitmap (category);
CREATE INDEX sales_2025_03_categories_idx
    ON sales_2025_03 USING bitmap (category);

После создания индексов присоедините их к родительскому индексу с помощью ALTER INDEX …​ ATTACH PARTITION:

ALTER INDEX sales_category_idx
    ATTACH PARTITION sales_2025_01_categories_idx;
ALTER INDEX sales_category_idx
    ATTACH PARTITION sales_2025_02_categories_idx;
ALTER INDEX sales_category_idx
    ATTACH PARTITION sales_2025_03_categories_idx;

После присоединения индексов для всех партиций родительский индекс автоматически помечается как корректный.

Проверка использования индекса

После создания индекса соберите статистику таблицы:

ANALYZE;

В следующем примере с помощью EXPLAIN показано, как Greengage DB выполняет запрос к партиционированной таблице и используется ли в нем индекс:

EXPLAIN (COSTS OFF)
SELECT SUM(amount) sum_amount
FROM sales
WHERE category = 'Online'
  AND date >= DATE '2025-03-10'
  AND date < DATE '2025-03-15';

Результат:

                                                         QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate
   ->  Gather Motion 4:1  (slice1; segments: 4)
         ->  Partial Aggregate
               ->  Dynamic Bitmap Heap Scan on sales
                     Number of partitions to scan: 1 (out of 3)
                     Recheck Cond: (category = 'Online'::text)
                     Filter: ((category = 'Online'::text) AND (date >= '2025-03-10'::date) AND (date < '2025-03-15'::date))
                     ->  Dynamic Bitmap Index Scan on sales_category_idx
                           Index Cond: (category = 'Online'::text)
 Optimizer: GPORCA
(10 rows)

В этом примере:

  • Number of partitions to scan: 1 (out of 3) — означает, что выполнено сканирование ограниченного числа партиций (partition elimination). На основе фильтра по дате Greengage DB сканирует только партицию со строками для указанного диапазона дат, а не все партиции таблицы.

  • Dynamic Bitmap Index Scan on sales_category_idx — означает, что оптимизатор использует индекс sales_category_idx для поиска подходящих строк.

Обмен партиции

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01 PARTITION OF sales
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02 PARTITION OF sales
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03 PARTITION OF sales
    FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

Обмен партиции заменяет существующую партицию другой таблицей с той же структурой. Эта операция выполняется путем отсоединения текущей партиции и присоединения новой таблицы вместо нее.

Создайте staging-таблицу для использования в качестве замещающей партиции:

CREATE TABLE stg_sales_2025_03
(
    LIKE sales
);

Отсоедините существующую партицию от партиционированной таблицы:

ALTER TABLE sales
    DETACH PARTITION sales_2025_03;

Присоедините новую таблицу в качестве партиции с теми же границами:

ALTER TABLE sales
    ATTACH PARTITION stg_sales_2025_03
        FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

Убедитесь, что новая таблица теперь является частью иерархии партиций:

SELECT partitiontablename,
       partitionrangestart,
       partitionrangeend
FROM gp_toolkit.gp_partitions
WHERE tablename = 'sales';

Результат:

 partitiontablename | partitionrangestart | partitionrangeend
--------------------+---------------------+-------------------
 sales_2025_01      | '2025-01-01'        | '2025-02-01'
 sales_2025_02      | '2025-02-01'        | '2025-03-01'
 stg_sales_2025_03  | '2025-03-01'        | '2025-04-01'
(3 rows)

Переименование партиции

Партиция переименовывается аналогично обычной таблице с использованием команды ALTER TABLE …​ RENAME TO.

В следующем примере переименовывается staging-таблица, которая используется как партиция в разделе Обмен партиции:

ALTER TABLE stg_sales_2025_03
    RENAME TO sales_2025_03;

Проверьте обновленные имена партиций:

SELECT partitiontablename,
       partitionrangestart,
       partitionrangeend
FROM gp_toolkit.gp_partitions
WHERE tablename = 'sales';

Результат:

 partitiontablename | partitionrangestart | partitionrangeend
--------------------+---------------------+-------------------
 sales_2025_01      | '2025-01-01'        | '2025-02-01'
 sales_2025_02      | '2025-02-01'        | '2025-03-01'
 sales_2025_03      | '2025-03-01'        | '2025-04-01'
(3 rows)

Удаление данных из партиции

CREATE TABLE sales
(
    id     INT,
    date   DATE,
    amount DECIMAL(10, 2)
)
    USING ao_row
    DISTRIBUTED BY (id)
    PARTITION BY RANGE (date);

CREATE TABLE sales_2025_01 PARTITION OF sales
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE sales_2025_02 PARTITION OF sales
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

CREATE TABLE sales_2025_03 PARTITION OF sales
    FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

Удаление данных из партиции выполняется так же, как удаление данных из обычной таблицы. Если у партиции есть сабпартиции, Greengage DB также автоматически удаляет данные во всех нижележащих сабпартициях.

Удалить данные из конкретной партиции:

TRUNCATE ONLY sales_2025_01;

Удалить данные из всей партиционированной таблицы:

TRUNCATE sales;

Удаление партиции

Один из способов удалить исторические данные — удалить более не требующуюся партицию:

DROP TABLE sales_2025_01;

Эта операция позволяет эффективно удалять большие объемы данных, так как не выполняется удаление строк по отдельности. Обратите внимание, что для этой команды требуется блокировка уровня ACCESS EXCLUSIVE на родительской таблице.

Также можно удалить партицию из партиционированной таблицы, сохранив ее как отдельную таблицу. Для этого используется команда ALTER TABLE …​ DETACH PARTITION, которая отсоединяет партицию от партиционированной таблицы.

Ограничения

Общие ограничения

К партиционированным таблицам Greengage DB применяются следующие ограничения:

  • Партиционированная таблица может иметь не более 32767 партиций на каждом уровне иерархии.

  • Таблицы, созданные с использованием политики распределения данных DISTRIBUTED REPLICATED, не могут быть партиционированы.

  • Оптимизатор запросов GPORCA не поддерживает однородные многоуровневые партиционированные таблицы. Если GPORCA включен (по умолчанию), то при работе с многоуровневыми партиционированными таблицами запросы выполняются с использованием планировщика PostgreSQL.

  • Утилита gpbackup не создает резервную копию данных листовой партиции, если эта партиция является внешней или сторонней таблицей.

  • Чтобы создать ограничение уникальности или первичного ключа для партиционированной таблицы, столбцы ключа партиционирования не должны содержать выражения или вызовы функций, а само ограничение должно включать все столбцы ключа партиционирования. Это необходимо, поскольку уникальность обеспечивается только внутри отдельных партиций; уникальность между партициями должна гарантироваться самой схемой партиционирования.

  • Ограничения-исключения нельзя определить для всей партиционированной таблицы. Их можно задавать только для отдельных листовых партиций. Это связано с тем, что невозможно гарантировать соблюдение таких ограничений между партициями.

  • Временные и постоянные объекты нельзя смешивать в одной иерархии партиций. Если родительская таблица является постоянной, все партиции также должны быть постоянными; то же правило действует и для временных таблиц. При использовании временных партиционированных таблиц все объекты в иерархии должны существовать в рамках одной сессии.

Модель наследования партиционированных таблиц

Отдельные партиции внутренне связаны со своей партиционированной таблицей с помощью механизма наследования. Однако для декларативно партиционированных таблиц и их партиций поддерживаются не все возможности обычного наследования таблиц. В частности, партиция не может иметь родителя, отличного от собственной партиционированной таблицы, а таблица не может одновременно наследоваться от партиционированной и обычной таблицы. Партиционированные таблицы и их партиции не входят в общую иерархию наследования с обычными таблицами.

Поскольку иерархия партиций (партиционированная таблица и ее партиции) реализована с использованием наследования, применяются tableoid и стандартные правила наследования, описанные в документации PostgreSQL в разделе Inheritance, со следующими исключениями:

  • Партиции не могут содержать столбцы, отсутствующие в родительской таблице. Столбцы нельзя задавать при создании партиции с помощью CREATE TABLE, а также нельзя изменять партиции для добавления столбцов с помощью ALTER TABLE. Таблицу можно присоединить как партицию с помощью ALTER TABLE …​ ATTACH PARTITION только в том случае, если ее определение столбцов полностью совпадает с родительской таблицей.

  • Ограничения CHECK и NOT NULL для партиционированной таблицы всегда наследуются всеми партициями. Greengage DB не допускает использования ограничений CHECK, помеченных как NO INHERIT, для партиционированных таблиц. Ограничение NOT NULL нельзя удалить из столбца партиции, если оно определено в родительской таблице.

  • Ключевое слово ONLY можно использовать для добавления или удаления ограничений в партиционированной таблице только при отсутствии партиций. После создания партиций использование ONLY приводит к ошибке. В этом случае ограничениями необходимо управлять непосредственно в отдельных партициях (если они не заданы в родительской таблице).

  • Поскольку сама партиционированная таблица не содержит данных, операция TRUNCATE ONLY для партиционированной таблицы не поддерживается и возвращает ошибку.

Ограничения для внешних и сторонних листовых партиций

Если листовая партиция является внешней или сторонней таблицей, действуют следующие ограничения:

  • Если внешняя или сторонняя таблица, используемая как партиция, не является пишущей либо у пользователя нет прав на ее изменение, команды изменения данных (INSERT, UPDATE, DELETE или TRUNCATE) для этой партиции завершаются ошибкой.

  • Команда COPY не может копировать данные в партиционированную таблицу, если при этом загружаемые данные попадут во внешнюю или стороннюю партицию.

  • Команда COPY при чтении из партиционированной таблицы возвращает ошибку, если встречает внешнюю или стороннюю партицию, за исключением случая использования выражения IGNORE EXTERNAL PARTITIONS. Если это выражение указано, Greengage DB пропускает внешние и сторонние партиции и не копирует данные из них.

    Чтобы использовать команду COPY для партиционированной таблицы с листовой партицией, которая является внешней или сторонней таблицей, укажите SQL-выражение вместо имени партиционированной таблицы для копирования данных. Например, если таблица sales содержит листовую партицию, которая является внешней таблицей, следующая команда отправляет данные в стандартный вывод (stdout):

    COPY (SELECT * FROM sales) TO stdout;
  • Если внешняя или сторонняя таблица-партиция не является пишущей либо у пользователя нет прав на запись в таблицу, Greengage DB возвращает ошибку при следующих операциях:

    • Добавление или удаление столбца.

    • Изменение типа данных столбца.

Неподдерживаемые операции ALTER TABLE (классический синтаксис)

Эти операции ALTER TABLE …​ ALTER PARTITION не поддерживаются, если партиционированная таблица содержит внешнюю или стороннюю таблицу-партицию:

  • Задание шаблона сабпартиции.

  • Изменение свойств партиции.

  • Создание партиции по умолчанию.

  • Задание политики распределения.

  • Добавление или удаление ограничения NOT NULL для столбца.

  • Добавление или удаление ограничений.

  • Разделение внешней партиции.