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

Обзор

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

Принадлежность каждой новой записи таблицы к той или иной партиции определяется на основе значения ключа партиционирования (partition key) — столбца (или набора столбцов в случае многоуровневого партиционирования), который выбирается при создании партиционированной таблицы.

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

book dark
Партиционирование и распределение данных
book light
Партиционирование и распределение данных

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

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

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

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

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

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

Ограничения партиционирования

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

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

  • Если партиционируемая таблица содержит первичный ключ или столбец с ограничением UNIQUE — они должны быть указаны в ключе партиционирования.

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

  • Загрузка данных в партиционированные таблицы крайне неэффективна. Поэтому рекомендуется загружать данные в промежуточную (staging) таблицу и затем применять к партиционированной таблице команду EXCHANGE PARTITION.

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

    • Immutable-операторы, такие как: =, <, <=, >, >= и <>.

    • Функции с типом STABLE и IMMUTABLE (не VOLATILE).

  • Существует ряд дополнительных ограничений для партиционированных таблиц, в которых дочерние партиции являются внешними (external) таблицами.

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

Синтаксис

Для создания партиционированной таблицы используется команда CREATE TABLE с выражением PARTITION BY (и опционально SUBPARTITION BY). Синтаксис команды зависит от типа партиционирования: range, list или комбинация обоих типов (multi-level):

  • Партиционирование на основе диапазонов (range). Партиционирование с типом range осуществляет разделение данных на основе числовых или временных (date/timestamp) диапазонов. Необходимо использовать выражение PARTITION BY RANGE, после него указать в скобках ключ партиционирования и затем в отдельных скобках описать интервалы для партиций, используя ключевые слова START и END. Выражения INCLUSIVE и EXCLUSIVE используются в связке с START и END для указания того, должны ли попадать в соответствующий диапазон граничные значения. По умолчанию значения, указанные с помощью START, включаются в диапазон; значения, определенные с помощью END — нет.

     

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

    • Автоматическое формирование партиций. Чтобы сгенерировать партиции автоматически, необходимо использовать ключевые слова START и END один раз для определения границ всего диапазона. Затем требуется указать выражение EVERY для определения шага инкремента. При использовании диапазона дат это выражение требует указания единицы измерения: day, month или year (например, EVERY(INTERVAL '1 month')). Для числовых диапазонов вводится только число (например, EVERY(1)). СУБД автоматически выполнит разбиение всего указанного интервала на нужное число партиций в соответствии с выбранным шагом.

    • Определение партиций вручную. Альтернативный способ описания range-партиций — вручную. В этом случае необходимо описать спецификации всех партиций через запятую в круглых скобках после указания ключа партиционирования. Каждая спецификация начинается с выражения PARTITION <partition_name>, где <partition_name> — имя, используемое вместо автоматически генерируемого номера в полном имени партиции. Оставшаяся часть спецификации содержит выражения START и END, определяющие границы каждой партиции. Обратите внимание, что указание END требуется только для партиции, идущей в списке последней.

  • Партиционирование на основе списков значений (list). Партиционирование с типом list выполняет разделение данных на основе предопределенного списка значений ключа партиционирования. Необходимо использовать выражение PARTITION BY LIST, после него указать в скобках ключ партиционирования и затем в отдельных скобках описать спецификации партиций через запятую. Каждая спецификация использует следующий шаблон: PARTITION <partition_name> VALUES (<list_value> [,…​]), где <partition_name> — имя, используемое вместо автоматически генерируемого номера в полном имени партиции; <list_value> — значения ключа партиционирования, соответствующие текущей партиции.

  • Многоуровневое партиционирование (multi-level). Партиционирование с типом multi-level — это комбинация партиционирования с типом range и/или list на нескольких уровнях. Оно позволяет разделять партиции первого уровня на сабпартиции (subpartitions) второго уровня, а их в свою очередь — на сабпартиции третьего уровня и так далее. Выражение PARTITION BY (RANGE или LIST) используется для определения ключа партиционирования на первом уровне иерархии; выражение SUBPARTITION BY (RANGE или LIST) — на всех нижележащих уровнях. Спецификации сабпартиций не отличаются от партиций, за исключением использования ключевого слова SUBPARTITION вместо PARTITION.

     

    Возможны два способа определения сабпартиций — с использованием шаблона (subpartition template) и без него:

    • Использование subpartition template. В этом случае спецификации сабпартиций каждого уровня описываются только один раз с использованием выражения SUBPARTITION TEMPLATE. Каждый шаблон заполняется после соответствующего выражения SUBPARTITION BY. Использование шаблонов гарантирует, что все партиции будут иметь одинаковую структуру, включая те, что будут добавлены после создания таблицы.

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

Для всех видов партиционирования возможно указание партиции по умолчанию (default partition) с использованием выражения DEFAULT PARTITION (или DEFAULT SUBPARTITION для сабпартиций) сразу после спецификации партиции (или сабпартиции). Этот тип партиций предназначен для хранения данных, которые не попадают в указанный диапазон (или список) значений.

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

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

Примеры

Партиционирование на основе диапазона дат (автоматически)

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

CREATE TABLE book_order
  (id INT,
   book_id INT,
   client_id INT,
   book_count SMALLINT,
   order_date DATE,
   CHECK(book_count >= 1)
  )
WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
DISTRIBUTED BY(id)
PARTITION BY RANGE(order_date)
(START(date '2022-01-01') INCLUSIVE
 END(date '2023-01-01') EXCLUSIVE
 EVERY(INTERVAL '1 month'),
 DEFAULT PARTITION other);

Результат:

NOTICE:  CREATE TABLE will create partition "book_order_1_prt_other" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_2" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_3" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_4" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_5" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_6" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_7" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_8" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_9" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_10" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_11" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_12" for table "book_order"
NOTICE:  CREATE TABLE will create partition "book_order_1_prt_13" for table "book_order"
CREATE TABLE
Партиционирование на основе числового диапазона (автоматически)

 
Следующий пример показывает создание десяти партиций на основе числового диапазона — по одной партиции на каждый год с 2013 по 2022. Также создается партиция по умолчанию. По сравнению с партиционированием на основе диапазона дат, нет необходимости явно указывать тип данных в выражениях START и END, а также выбирать единицу измерения в выражении EVERY. В остальном синтакис тот же.

CREATE TABLE book
  (id INT,
   title TEXT,
   author_id INT NOT NULL,
   public_year SMALLINT NULL,
   type_id INT NOT NULL,
   cover_id INT NOT NULL)
WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
DISTRIBUTED BY(id)
PARTITION BY RANGE(public_year)
(START(2013)
 END(2023)
 EVERY(1),
 DEFAULT PARTITION other);

Результат:

NOTICE:  CREATE TABLE will create partition "book_1_prt_other" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_2" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_3" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_4" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_5" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_6" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_7" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_8" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_9" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_10" for table "book"
NOTICE:  CREATE TABLE will create partition "book_1_prt_11" for table "book"
CREATE TABLE
Партиционирование на основе диапазона дат (вручную)

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

CREATE TABLE book_order_manual
  (id INT,
   book_id INT,
   client_id INT,
   book_count SMALLINT,
   order_date DATE,
   CHECK(book_count >= 1)
  )
WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
DISTRIBUTED BY(id)
PARTITION BY RANGE(order_date)
(PARTITION Jan22 START(date '2022-01-01') INCLUSIVE,
 PARTITION Feb22 START(date '2022-02-01') INCLUSIVE,
 PARTITION Mar22 START(date '2022-03-01') INCLUSIVE,
 PARTITION Apr22 START(date '2022-04-01') INCLUSIVE,
 PARTITION May22 START(date '2022-05-01') INCLUSIVE,
 PARTITION Jun22 START(date '2022-06-01') INCLUSIVE,
 PARTITION Jul22 START(date '2022-07-01') INCLUSIVE,
 PARTITION Aug22 START(date '2022-08-01') INCLUSIVE,
 PARTITION Sep22 START(date '2022-09-01') INCLUSIVE,
 PARTITION Oct22 START(date '2022-10-01') INCLUSIVE,
 PARTITION Nov22 START(date '2022-11-01') INCLUSIVE,
 PARTITION Dec22 START(date '2022-12-01') INCLUSIVE END(date '2023-01-01') EXCLUSIVE,
 DEFAULT PARTITION other);

Результат:

NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_other" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_jan22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_feb22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_mar22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_apr22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_may22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_jun22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_jul22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_aug22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_sep22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_oct22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_nov22" for table "book_order_manual"
NOTICE:  CREATE TABLE will create partition "book_order_manual_1_prt_dec22" for table "book_order_manual"
CREATE TABLE
Партиционирование на основе числового диапазона (вручную)

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

CREATE TABLE book_manual
  (id INT,
   title TEXT,
   author_id INT NOT NULL,
   public_year SMALLINT NULL,
   type_id INT NOT NULL,
   cover_id INT NOT NULL)
WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
DISTRIBUTED BY(id)
PARTITION BY RANGE(public_year)
(PARTITION Year2013 START(2013),
 PARTITION Year2014 START(2014),
 PARTITION Year2015 START(2015),
 PARTITION Year2016 START(2016),
 PARTITION Year2017 START(2017),
 PARTITION Year2018 START(2018),
 PARTITION Year2019 START(2019),
 PARTITION Year2020 START(2020),
 PARTITION Year2021 START(2021),
 PARTITION Year2022 START(2022) END(2023),
 DEFAULT PARTITION other);

Результат:

NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_other" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2013" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2014" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2015" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2016" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2017" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2018" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2019" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2020" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2021" for table "book_manual"
NOTICE:  CREATE TABLE will create partition "book_manual_1_prt_year2022" for table "book_manual"
CREATE TABLE
Партиционирование на основе списка значений

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

CREATE TABLE client (id INT, name TEXT, gender CHAR(1))
DISTRIBUTED BY (id)
PARTITION BY LIST (gender)
(PARTITION girls VALUES ('F'),
 PARTITION boys VALUES ('M'),
 DEFAULT PARTITION other);

Результат:

NOTICE:  CREATE TABLE will create partition "client_1_prt_girls" for table "client"
NOTICE:  CREATE TABLE will create partition "client_1_prt_boys" for table "client"
NOTICE:  CREATE TABLE will create partition "client_1_prt_other" for table "client"
CREATE TABLE
Многоуровневое партиционирование (subpartition template)

 
Следующий пример демонстрирует трехуровневое партиционирование. Команда добавляет три партиции с типом range на первом уровне иерархии, затем разделяет их на три сабпартиции на втором уровне — в зависимости от значений столбца region. Эти сабпартиции, в свою очередь, разделяются на три сабпартиции на третьем уровне — на основе значений столбца fiction. На каждом уровне иерархии добавляется партиция/сабпартиция по умолчанию.

CREATE TABLE sales
 (id INT,
  date DATE,
  fiction CHAR(1),
  region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
   SUBPARTITION BY LIST (region)
     SUBPARTITION TEMPLATE (
       SUBPARTITION rus VALUES ('rus'),
       SUBPARTITION asia VALUES ('asia'),
       SUBPARTITION europe VALUES ('europe'),
       DEFAULT SUBPARTITION other_rg
     )

     SUBPARTITION BY LIST (fiction)
       SUBPARTITION TEMPLATE (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       )

(START(date '2022-01-01') INCLUSIVE
 END(date '2022-04-01') EXCLUSIVE
 EVERY(INTERVAL '1 month'),
 DEFAULT PARTITION other_dt
);

Результат:

NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt" for table "sales"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_rus" for table "sales_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_rus_3_prt_fiction" for table "sales_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_rus_3_prt_non_fiction" for table "sales_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_rus_3_prt_other_tp" for table "sales_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_asia" for table "sales_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_asia_3_prt_fiction" for table "sales_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_asia_3_prt_non_fiction" for table "sales_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_asia_3_prt_other_tp" for table "sales_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_europe" for table "sales_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_europe_3_prt_fiction" for table "sales_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_europe_3_prt_non_fiction" for table "sales_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_europe_3_prt_other_tp" for table "sales_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_other_rg" for table "sales_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_other_rg_3_prt_fiction" for table "sales_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_other_rg_3_prt_non_fiction" for table "sales_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp" for table "sales_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2" for table "sales"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_rus" for table "sales_1_prt_2"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_rus_3_prt_fiction" for table "sales_1_prt_2_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_rus_3_prt_non_fiction" for table "sales_1_prt_2_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_rus_3_prt_other_tp" for table "sales_1_prt_2_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_asia" for table "sales_1_prt_2"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_asia_3_prt_fiction" for table "sales_1_prt_2_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_asia_3_prt_non_fiction" for table "sales_1_prt_2_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_asia_3_prt_other_tp" for table "sales_1_prt_2_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_europe" for table "sales_1_prt_2"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_europe_3_prt_fiction" for table "sales_1_prt_2_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_europe_3_prt_non_fiction" for table "sales_1_prt_2_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_europe_3_prt_other_tp" for table "sales_1_prt_2_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_other_rg" for table "sales_1_prt_2"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_other_rg_3_prt_fiction" for table "sales_1_prt_2_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_other_rg_3_prt_non_fiction" for table "sales_1_prt_2_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_2_2_prt_other_rg_3_prt_other_tp" for table "sales_1_prt_2_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3" for table "sales"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_rus" for table "sales_1_prt_3"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_rus_3_prt_fiction" for table "sales_1_prt_3_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_rus_3_prt_non_fiction" for table "sales_1_prt_3_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_rus_3_prt_other_tp" for table "sales_1_prt_3_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_asia" for table "sales_1_prt_3"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_asia_3_prt_fiction" for table "sales_1_prt_3_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_asia_3_prt_non_fiction" for table "sales_1_prt_3_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_asia_3_prt_other_tp" for table "sales_1_prt_3_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_europe" for table "sales_1_prt_3"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_europe_3_prt_fiction" for table "sales_1_prt_3_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_europe_3_prt_non_fiction" for table "sales_1_prt_3_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_europe_3_prt_other_tp" for table "sales_1_prt_3_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_other_rg" for table "sales_1_prt_3"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_other_rg_3_prt_fiction" for table "sales_1_prt_3_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_other_rg_3_prt_non_fiction" for table "sales_1_prt_3_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_3_2_prt_other_rg_3_prt_other_tp" for table "sales_1_prt_3_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4" for table "sales"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_rus" for table "sales_1_prt_4"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_rus_3_prt_fiction" for table "sales_1_prt_4_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_rus_3_prt_non_fiction" for table "sales_1_prt_4_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_rus_3_prt_other_tp" for table "sales_1_prt_4_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_asia" for table "sales_1_prt_4"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_asia_3_prt_fiction" for table "sales_1_prt_4_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_asia_3_prt_non_fiction" for table "sales_1_prt_4_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_asia_3_prt_other_tp" for table "sales_1_prt_4_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_europe" for table "sales_1_prt_4"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_europe_3_prt_fiction" for table "sales_1_prt_4_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_europe_3_prt_non_fiction" for table "sales_1_prt_4_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_europe_3_prt_other_tp" for table "sales_1_prt_4_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_other_rg" for table "sales_1_prt_4"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_other_rg_3_prt_fiction" for table "sales_1_prt_4_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_other_rg_3_prt_non_fiction" for table "sales_1_prt_4_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_1_prt_4_2_prt_other_rg_3_prt_other_tp" for table "sales_1_prt_4_2_prt_other_rg"
CREATE TABLE
Многоуровневое партиционирование (вручную)

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

CREATE TABLE sales_manual
 (id INT,
  date DATE,
  fiction CHAR(1),
  region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
  SUBPARTITION BY LIST (region)
    SUBPARTITION BY LIST (fiction)

(PARTITION Jan2022 START(date '2022-01-01') INCLUSIVE
  (
    SUBPARTITION rus VALUES ('rus')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION asia VALUES ('asia')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION europe VALUES ('europe')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    DEFAULT SUBPARTITION other_rg
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       )
  ),
 PARTITION Feb2022 START(date '2022-02-01') INCLUSIVE
  (
    SUBPARTITION rus VALUES ('rus')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION asia VALUES ('asia')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION europe VALUES ('europe')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    DEFAULT SUBPARTITION other_rg
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       )
  ),
 PARTITION Mar2022 START(date '2022-03-01') INCLUSIVE
  (
    SUBPARTITION rus VALUES ('rus')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION asia VALUES ('asia')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION europe VALUES ('europe')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    DEFAULT SUBPARTITION other_rg
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       )
  ),
 DEFAULT PARTITION other_dt
  (
    SUBPARTITION rus VALUES ('rus')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION asia VALUES ('asia')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    SUBPARTITION europe VALUES ('europe')
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       ),
    DEFAULT SUBPARTITION other_rg
       (
         SUBPARTITION fiction VALUES ('f'),
         SUBPARTITION non_fiction VALUES ('n'),
         DEFAULT SUBPARTITION other_tp
       )
  )
);

Результат:

NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt" for table "sales_manual"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_rus" for table "sales_manual_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_rus_3_prt_fiction" for table "sales_manual_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_rus_3_prt_non_fiction" for table "sales_manual_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_rus_3_prt_other_tp" for table "sales_manual_1_prt_other_dt_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_asia" for table "sales_manual_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_asia_3_prt_fiction" for table "sales_manual_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_asia_3_prt_non_fiction" for table "sales_manual_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_asia_3_prt_other_tp" for table "sales_manual_1_prt_other_dt_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_europe" for table "sales_manual_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_europe_3_prt_fiction" for table "sales_manual_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_europe_3_prt_non_fiction" for table "sales_manual_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_europe_3_prt_other_tp" for table "sales_manual_1_prt_other_dt_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_other_rg" for table "sales_manual_1_prt_other_dt"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_fiction" for table "sales_manual_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_non_fiction" for table "sales_manual_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp" for table "sales_manual_1_prt_other_dt_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022" for table "sales_manual"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_rus" for table "sales_manual_1_prt_jan2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_rus_3_prt_fiction" for table "sales_manual_1_prt_jan2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_rus_3_prt_non_fiction" for table "sales_manual_1_prt_jan2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_rus_3_prt_other_tp" for table "sales_manual_1_prt_jan2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_asia" for table "sales_manual_1_prt_jan2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction" for table "sales_manual_1_prt_jan2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_asia_3_prt_non_fiction" for table "sales_manual_1_prt_jan2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_asia_3_prt_other_tp" for table "sales_manual_1_prt_jan2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_europe" for table "sales_manual_1_prt_jan2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_europe_3_prt_fiction" for table "sales_manual_1_prt_jan2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_europe_3_prt_non_fiction" for table "sales_manual_1_prt_jan2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_europe_3_prt_other_tp" for table "sales_manual_1_prt_jan2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_other_rg" for table "sales_manual_1_prt_jan2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_fiction" for table "sales_manual_1_prt_jan2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_non_fiction" for table "sales_manual_1_prt_jan2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_other_tp" for table "sales_manual_1_prt_jan2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022" for table "sales_manual"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_rus" for table "sales_manual_1_prt_feb2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_rus_3_prt_fiction" for table "sales_manual_1_prt_feb2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_rus_3_prt_non_fiction" for table "sales_manual_1_prt_feb2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_rus_3_prt_other_tp" for table "sales_manual_1_prt_feb2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_asia" for table "sales_manual_1_prt_feb2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_asia_3_prt_fiction" for table "sales_manual_1_prt_feb2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_asia_3_prt_non_fiction" for table "sales_manual_1_prt_feb2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_asia_3_prt_other_tp" for table "sales_manual_1_prt_feb2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_europe" for table "sales_manual_1_prt_feb2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_europe_3_prt_fiction" for table "sales_manual_1_prt_feb2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_europe_3_prt_non_fiction" for table "sales_manual_1_prt_feb2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_europe_3_prt_other_tp" for table "sales_manual_1_prt_feb2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_other_rg" for table "sales_manual_1_prt_feb2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_fiction" for table "sales_manual_1_prt_feb2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_non_fiction" for table "sales_manual_1_prt_feb2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_other_tp" for table "sales_manual_1_prt_feb2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022" for table "sales_manual"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_rus" for table "sales_manual_1_prt_mar2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_rus_3_prt_fiction" for table "sales_manual_1_prt_mar2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_rus_3_prt_non_fiction" for table "sales_manual_1_prt_mar2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_rus_3_prt_other_tp" for table "sales_manual_1_prt_mar2022_2_prt_rus"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_asia" for table "sales_manual_1_prt_mar2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_asia_3_prt_fiction" for table "sales_manual_1_prt_mar2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_asia_3_prt_non_fiction" for table "sales_manual_1_prt_mar2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_asia_3_prt_other_tp" for table "sales_manual_1_prt_mar2022_2_prt_asia"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_europe" for table "sales_manual_1_prt_mar2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_europe_3_prt_fiction" for table "sales_manual_1_prt_mar2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_europe_3_prt_non_fiction" for table "sales_manual_1_prt_mar2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_europe_3_prt_other_tp" for table "sales_manual_1_prt_mar2022_2_prt_europe"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_other_rg" for table "sales_manual_1_prt_mar2022"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_fiction" for table "sales_manual_1_prt_mar2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_non_fiction" for table "sales_manual_1_prt_mar2022_2_prt_other_rg"
NOTICE:  CREATE TABLE will create partition "sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_other_tp" for table "sales_manual_1_prt_mar2022_2_prt_other_rg"
CREATE TABLE

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

Независимо от типа партиционирования, Greenplum создает верхнеуровневую (родительскую) таблицу с одним или несколькими уровнями дочерних таблиц. Между родительской и дочерними таблицами устанавливается отношение наследования, схожее по функциональности с INHERITS в PostgreSQL. Если при использовании psql запустить команды \dt или \dt+ для вывода списка отношений БД, можно убедиться, что все партиции и сабпартиции отобразятся в списке подобно обычным таблицам.

Родительские таблицы всегда пусты. Данные хранятся в дочерних партициях, при использовании многоуровневого партиционирования — в сабпартициях самого нижнего уровня (leaf-партициях).

Для проверки того, какие данные могут быть добавлены в партиции, Greenplum использует ограничения CHECK. Строки, не удовлетворяющие спецификациям ни одной нижнеуровневой партиции, отклоняются, и загрузка данных завершается с ошибкой (при условии, что не определена партиция по умолчанию). Оптимизатор запросов также использует ограничения CHECK для сканирования партиций, удовлетворяющих условиям предиката.

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

Например, можно добавить данные в таблицу sales_manual, созданную выше (см. Многоуровневое партиционирование (вручную)):

INSERT INTO sales_manual(id, date, fiction, region)
VALUES(1, date '2022-01-01', 'f', 'asia');

Также возможно добавить данные непосредственно в нижнеуровневую сабпартицию sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction:

INSERT INTO sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction(id, date, fiction, region)
VALUES(2, date '2022-01-02', 'f', 'asia');

Однако нельзя добавить данные в партицию, не являющуюся нижнеуровневой (например, sales_manual_1_prt_jan2022):

INSERT INTO sales_manual_1_prt_jan2022(id, date, fiction, region) VALUES(3, date '2022-01-03', 'f', 'asia');

Запрос возвращает ошибку:

ERROR:  directly modifying intermediate part of a partitioned table is disallowed
HINT:  Modify either the root or a leaf partition instead.

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

INSERT INTO sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction(id, date, fiction, region)
VALUES(4, date '2022-01-02', 'n', 'asia');

Результат:

ERROR:  trying to insert row into wrong partition  (seg7 10.92.16.100:10007 pid=19273)
DETAIL:  Expected partition: sales_manual_1_prt_jan2022_2_prt_asia_3_prt_non_fiction, provided partition: sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction.

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

SELECT * FROM  sales_manual;
SELECT * FROM  sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;

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

 id |    date    | fiction | region
----+------------+---------+--------
  1 | 2022-01-01 | f       | asia
  2 | 2022-01-02 | f       | asia
(2 rows)

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

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

  1. Создайте исходную таблицу без партиционирования:

    CREATE TABLE genre (id INT, name TEXT NOT NULL, fiction CHAR(1) NOT NULL)
    DISTRIBUTED BY (id);
  2. Создайте партиционированную таблицу с аналогичной структурой — с помощью выражения LIKE:

    CREATE TABLE genre_partitioned (LIKE genre)
    PARTITION BY LIST (fiction)
    (PARTITION fiction VALUES ('F'),
     PARTITION non_fiction VALUES ('N'),
     DEFAULT PARTITION other
    );

    Результат:

    NOTICE:  table doesn't have 'DISTRIBUTED BY' clause, defaulting to distribution columns from LIKE table
    NOTICE:  CREATE TABLE will create partition "genre_partitioned_1_prt_fiction" for table "genre_partitioned"
    NOTICE:  CREATE TABLE will create partition "genre_partitioned_1_prt_non_fiction" for table "genre_partitioned"
    NOTICE:  CREATE TABLE will create partition "genre_partitioned_1_prt_other" for table "genre_partitioned"
    CREATE TABLE
    ВАЖНО

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

  3. Загрузите данные из исходной таблицы в партиционированную:

    INSERT INTO genre_partitioned SELECT * FROM genre;
  4. Удалите исходную таблицу:

    DROP TABLE genre;
  5. Назначьте имя удаленной таблицы партиционированной таблице:

    ALTER TABLE genre_partitioned RENAME TO genre;
  6. Переопределите все необходимые разрешения для партиционированной таблицы. Например:

    GRANT ALL PRIVILEGES ON genre TO gpadmin;

Просмотр партиционированных таблиц

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

  • Чтобы вывести структуру определенной таблицы, можно сделать выборку из системного представления pg_catalog.pg_partitions. Вместо <schema_name> следует использовать имя схемы, вместо <table_name>  — имя родительской таблицы.

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = '<schema_name>'
    AND   tablename  = '<table_name>';

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

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

    • partitionname — имя партиции, которое назначается при создании и используется для обращения к партиции в командах ALTER TABLE;

    • partitiontype — тип партиционирования (range или list);

    • partitionlevel — уровень партиции (начиная с 0 для партиций первого уровня);

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

     

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

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

    Таблица с типом партиционирования range
    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'book_order';

    Результат:

       partitiontablename   | partitionname | partitiontype | partitionlevel | partitionrank
    ------------------------+---------------+---------------+----------------+---------------
     book_order_1_prt_2     |               | range         |              0 |             1
     book_order_1_prt_3     |               | range         |              0 |             2
     book_order_1_prt_4     |               | range         |              0 |             3
     book_order_1_prt_5     |               | range         |              0 |             4
     book_order_1_prt_6     |               | range         |              0 |             5
     book_order_1_prt_7     |               | range         |              0 |             6
     book_order_1_prt_8     |               | range         |              0 |             7
     book_order_1_prt_9     |               | range         |              0 |             8
     book_order_1_prt_10    |               | range         |              0 |             9
     book_order_1_prt_11    |               | range         |              0 |            10
     book_order_1_prt_12    |               | range         |              0 |            11
     book_order_1_prt_13    |               | range         |              0 |            12
     book_order_1_prt_other | other         | range         |              0 |
    (13 rows)
    Таблица с типом партиционирования list
    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'client';

    Результат:

     partitiontablename | partitionname | partitiontype | partitionlevel | partitionrank
    --------------------+---------------+---------------+----------------+---------------
     client_1_prt_girls | girls         | list          |              0 |
     client_1_prt_boys  | boys          | list          |              0 |
     client_1_prt_other | other         | list          |              0 |
    (3 rows)
    Таблица с многоуровневым партиционированием (multi-level)
    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'sales'
    ORDER BY partitiontablename;

    Результат:

                      partitiontablename                   | partitionname | partitiontype | partitionlevel | partitionrank
    -------------------------------------------------------+---------------+---------------+----------------+---------------
     sales_1_prt_2                                         |               | range         |              0 |             1
     sales_1_prt_2_2_prt_asia                              | asia          | list          |              1 |
     sales_1_prt_2_2_prt_asia_3_prt_fiction                | fiction       | list          |              2 |
     sales_1_prt_2_2_prt_asia_3_prt_non_fiction            | non_fiction   | list          |              2 |
     sales_1_prt_2_2_prt_asia_3_prt_other_tp               | other_tp      | list          |              2 |
     sales_1_prt_2_2_prt_europe                            | europe        | list          |              1 |
     sales_1_prt_2_2_prt_europe_3_prt_fiction              | fiction       | list          |              2 |
     sales_1_prt_2_2_prt_europe_3_prt_non_fiction          | non_fiction   | list          |              2 |
     sales_1_prt_2_2_prt_europe_3_prt_other_tp             | other_tp      | list          |              2 |
     sales_1_prt_2_2_prt_other_rg                          | other_rg      | list          |              1 |
     sales_1_prt_2_2_prt_other_rg_3_prt_fiction            | fiction       | list          |              2 |
     sales_1_prt_2_2_prt_other_rg_3_prt_non_fiction        | non_fiction   | list          |              2 |
     sales_1_prt_2_2_prt_other_rg_3_prt_other_tp           | other_tp      | list          |              2 |
     sales_1_prt_2_2_prt_rus                               | rus           | list          |              1 |
     sales_1_prt_2_2_prt_rus_3_prt_fiction                 | fiction       | list          |              2 |
     sales_1_prt_2_2_prt_rus_3_prt_non_fiction             | non_fiction   | list          |              2 |
     sales_1_prt_2_2_prt_rus_3_prt_other_tp                | other_tp      | list          |              2 |
     sales_1_prt_3                                         |               | range         |              0 |             2
     sales_1_prt_3_2_prt_asia                              | asia          | list          |              1 |
     sales_1_prt_3_2_prt_asia_3_prt_fiction                | fiction       | list          |              2 |
     sales_1_prt_3_2_prt_asia_3_prt_non_fiction            | non_fiction   | list          |              2 |
     sales_1_prt_3_2_prt_asia_3_prt_other_tp               | other_tp      | list          |              2 |
     sales_1_prt_3_2_prt_europe                            | europe        | list          |              1 |
     sales_1_prt_3_2_prt_europe_3_prt_fiction              | fiction       | list          |              2 |
     sales_1_prt_3_2_prt_europe_3_prt_non_fiction          | non_fiction   | list          |              2 |
     sales_1_prt_3_2_prt_europe_3_prt_other_tp             | other_tp      | list          |              2 |
     sales_1_prt_3_2_prt_other_rg                          | other_rg      | list          |              1 |
     sales_1_prt_3_2_prt_other_rg_3_prt_fiction            | fiction       | list          |              2 |
     sales_1_prt_3_2_prt_other_rg_3_prt_non_fiction        | non_fiction   | list          |              2 |
     sales_1_prt_3_2_prt_other_rg_3_prt_other_tp           | other_tp      | list          |              2 |
     sales_1_prt_3_2_prt_rus                               | rus           | list          |              1 |
     sales_1_prt_3_2_prt_rus_3_prt_fiction                 | fiction       | list          |              2 |
     sales_1_prt_3_2_prt_rus_3_prt_non_fiction             | non_fiction   | list          |              2 |
     sales_1_prt_3_2_prt_rus_3_prt_other_tp                | other_tp      | list          |              2 |
     sales_1_prt_4                                         |               | range         |              0 |             3
     sales_1_prt_4_2_prt_asia                              | asia          | list          |              1 |
     sales_1_prt_4_2_prt_asia_3_prt_fiction                | fiction       | list          |              2 |
     sales_1_prt_4_2_prt_asia_3_prt_non_fiction            | non_fiction   | list          |              2 |
     sales_1_prt_4_2_prt_asia_3_prt_other_tp               | other_tp      | list          |              2 |
     sales_1_prt_4_2_prt_europe                            | europe        | list          |              1 |
     sales_1_prt_4_2_prt_europe_3_prt_fiction              | fiction       | list          |              2 |
     sales_1_prt_4_2_prt_europe_3_prt_non_fiction          | non_fiction   | list          |              2 |
     sales_1_prt_4_2_prt_europe_3_prt_other_tp             | other_tp      | list          |              2 |
     sales_1_prt_4_2_prt_other_rg                          | other_rg      | list          |              1 |
     sales_1_prt_4_2_prt_other_rg_3_prt_fiction            | fiction       | list          |              2 |
     sales_1_prt_4_2_prt_other_rg_3_prt_non_fiction        | non_fiction   | list          |              2 |
     sales_1_prt_4_2_prt_other_rg_3_prt_other_tp           | other_tp      | list          |              2 |
     sales_1_prt_4_2_prt_rus                               | rus           | list          |              1 |
     sales_1_prt_4_2_prt_rus_3_prt_fiction                 | fiction       | list          |              2 |
     sales_1_prt_4_2_prt_rus_3_prt_non_fiction             | non_fiction   | list          |              2 |
     sales_1_prt_4_2_prt_rus_3_prt_other_tp                | other_tp      | list          |              2 |
     sales_1_prt_other_dt                                  | other_dt      | range         |              0 |
     sales_1_prt_other_dt_2_prt_asia                       | asia          | list          |              1 |
     sales_1_prt_other_dt_2_prt_asia_3_prt_fiction         | fiction       | list          |              2 |
     sales_1_prt_other_dt_2_prt_asia_3_prt_non_fiction     | non_fiction   | list          |              2 |
     sales_1_prt_other_dt_2_prt_asia_3_prt_other_tp        | other_tp      | list          |              2 |
     sales_1_prt_other_dt_2_prt_europe                     | europe        | list          |              1 |
     sales_1_prt_other_dt_2_prt_europe_3_prt_fiction       | fiction       | list          |              2 |
     sales_1_prt_other_dt_2_prt_europe_3_prt_non_fiction   | non_fiction   | list          |              2 |
     sales_1_prt_other_dt_2_prt_europe_3_prt_other_tp      | other_tp      | list          |              2 |
     sales_1_prt_other_dt_2_prt_other_rg                   | other_rg      | list          |              1 |
     sales_1_prt_other_dt_2_prt_other_rg_3_prt_fiction     | fiction       | list          |              2 |
     sales_1_prt_other_dt_2_prt_other_rg_3_prt_non_fiction | non_fiction   | list          |              2 |
     sales_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp    | other_tp      | list          |              2 |
     sales_1_prt_other_dt_2_prt_rus                        | rus           | list          |              1 |
     sales_1_prt_other_dt_2_prt_rus_3_prt_fiction          | fiction       | list          |              2 |
     sales_1_prt_other_dt_2_prt_rus_3_prt_non_fiction      | non_fiction   | list          |              2 |
     sales_1_prt_other_dt_2_prt_rus_3_prt_other_tp         | other_tp      | list          |              2 |
    (68 rows)
  • Чтобы вывести все сабпартиции, созданные с помощью шаблона (subpartition template), можно сделать выборку из системного представления pg_catalog.pg_partition_templates.

    SELECT
      schemaname,
      tablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partition_templates;

    В дополнение к столбцам, описанным выше, этот запрос возвращает имя схемы <schemaname> и имя родительской таблицы <tablename>. При необходимости можно выбрать данные только для определенной таблицы с использованием выражения WHERE.

    Результат запроса
     schemaname | tablename | partitionname | partitiontype | partitionlevel | partitionrank
    ------------+-----------+---------------+---------------+----------------+---------------
     public     | sales     | rus           | list          |              1 |
     public     | sales     | asia          | list          |              1 |
     public     | sales     | europe        | list          |              1 |
     public     | sales     | other_rg      | list          |              1 |
     public     | sales     | fiction       | list          |              2 |
     public     | sales     | non_fiction   | list          |              2 |
     public     | sales     | other_tp      | list          |              2 |
    (7 rows)
  • Чтобы вывести все ключи партиционирования, можно обратиться к системному представлению pg_catalog.pg_partition_columns.

    SELECT * FROM pg_catalog.pg_partition_columns;

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

    • schemaname — имя схемы;

    • tablename — имя родительской таблицы;

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

    • partitionlevel — уровень партиции (начиная с 0 для партиций первого уровня);

    • position_in_partition_key — позиция столбца в составном ключе. Составные (multi-column) ключи партиционирования допускаются для партиций с типом list. По умолчанию, 1.

    Результат запроса
     schemaname |     tablename     | columnname  | partitionlevel | position_in_partition_key
    ------------+-------------------+-------------+----------------+---------------------------
     public     | book_order        | order_date  |              0 |                         1
     public     | book              | public_year |              0 |                         1
     public     | book_order_manual | order_date  |              0 |                         1
     public     | book_manual       | public_year |              0 |                         1
     public     | client            | gender      |              0 |                         1
     public     | sales             | date        |              0 |                         1
     public     | sales             | fiction     |              2 |                         1
     public     | sales             | fiction     |              2 |                         1
     public     | sales             | region      |              1 |                         1
     public     | sales             | region      |              1 |                         1
     public     | sales_manual      | date        |              0 |                         1
     public     | sales_manual      | fiction     |              2 |                         1
     public     | sales_manual      | region      |              1 |                         1
     public     | genre             | fiction     |              0 |                         1
    (14 rows)
  • Чтобы вывести партиционированные таблицы с перечислением всех уровней партиционирования, можно сделать выборку из таблицы системного каталога pg_catalog.pg_partition .

    SELECT
      t2.relname,
      t1.parkind,
      t1.parlevel,
      t1.paristemplate
    FROM pg_partition t1
    LEFT JOIN pg_class t2 ON t2.oid = t1.parrelid;

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

    • relname — имя родительской таблицы;

    • parkind — тип партиционирования (r для range, l для list);

    • parlevel — уровень партиции (начиная с 0 для партиций первого уровня);

    • paristemplate — признак того, является ли текущая строка определением шаблона (true) или описанием непосредственно уровня в иерархии партиционирования (false).

    Результат запроса
          relname      | parkind | parlevel | paristemplate
    -------------------+---------+----------+---------------
     book_order        | r       |        0 | f
     book              | r       |        0 | f
     book_order_manual | r       |        0 | f
     book_manual       | r       |        0 | f
     client            | l       |        0 | f
     sales             | l       |        2 | f
     sales             | l       |        2 | t
     sales             | l       |        1 | f
     sales             | l       |        1 | t
     sales             | r       |        0 | f
     sales_manual      | l       |        2 | f
     sales_manual      | l       |        1 | f
     sales_manual      | r       |        0 | f
     genre             | l       |        0 | f
    (14 rows)
ПРИМЕЧАНИЕ

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

Операции с партициями

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

 
Чтобы добавить новую партицию в партиционированную таблицу, необходимо использовать команду ALTER TABLE с выражением ADD PARTITION. Эту команду нельзя применять на одном уровне с партицией по умолчанию. Следующий пример показывает создание новой партиционированной таблицы и добавление нескольких партиций после создания:

  1. Создайте двухуровневую партиционированную таблицу без партиций по умолчанию:

    CREATE TABLE genre_stat
      (id SERIAL,
       jenre text NOT NULL,
       year INT NOT NULL,
       count INT NOT NULL)
    WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
    DISTRIBUTED BY(id)
    PARTITION BY RANGE (year)
     SUBPARTITION BY LIST (jenre)
      SUBPARTITION TEMPLATE (
        SUBPARTITION poetry VALUES ('poetry'),
        SUBPARTITION fantasy VALUES ('fantasy'),
        SUBPARTITION detective VALUES ('detective')
      )
    (START(2013)
     END(2015)
     EVERY(1));

    Результат:

    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_1" for table "genre_stat"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_1_2_prt_poetry" for table "genre_stat_1_prt_1"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_1_2_prt_fantasy" for table "genre_stat_1_prt_1"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_1_2_prt_detective" for table "genre_stat_1_prt_1"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_2" for table "genre_stat"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_2_2_prt_poetry" for table "genre_stat_1_prt_2"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_2_2_prt_fantasy" for table "genre_stat_1_prt_2"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_2_2_prt_detective" for table "genre_stat_1_prt_2"
    CREATE TABLE
  2. Добавьте новую партицию с помощью следующего запроса:

    ALTER TABLE genre_stat ADD PARTITION START(2012) END(2013);

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

    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637" for table "genre_stat"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637_2_prt_poetry" for table "genre_stat_1_prt_r188375637"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637_2_prt_fantasy" for table "genre_stat_1_prt_r188375637"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637_2_prt_detective" for table "genre_stat_1_prt_r188375637"
    ALTER TABLE
  3. Возможно также добавление сабпартиций в существующие партиции. Для этого необходимо указать полный путь к новой сабпартиции при помощи выражений ALTER PARTITION — по одному выражению для каждого уровня партиционирования, предшествующего новой сабпартиции:

    ALTER TABLE genre_stat ALTER PARTITION FOR (RANK(1))
          ADD PARTITION fantastic VALUES ('fantastic');

    Результат:

    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637_2_prt_fantastic" for table "genre_stat_1_prt_r188375637"
    ALTER TABLE
  4. Для просмотра обновленной структуры партиционированной таблицы используйте следующий запрос:

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'genre_stat' ORDER BY partitiontablename;

    Результат:

                 partitiontablename              | partitionname | partitiontype | partitionlevel | partitionrank
    ---------------------------------------------+---------------+---------------+----------------+---------------
     genre_stat_1_prt_1                          |               | range         |              0 |             2
     genre_stat_1_prt_1_2_prt_detective          | detective     | list          |              1 |
     genre_stat_1_prt_1_2_prt_fantasy            | fantasy       | list          |              1 |
     genre_stat_1_prt_1_2_prt_poetry             | poetry        | list          |              1 |
     genre_stat_1_prt_2                          |               | range         |              0 |             3
     genre_stat_1_prt_2_2_prt_detective          | detective     | list          |              1 |
     genre_stat_1_prt_2_2_prt_fantasy            | fantasy       | list          |              1 |
     genre_stat_1_prt_2_2_prt_poetry             | poetry        | list          |              1 |
     genre_stat_1_prt_r188375637                 |               | range         |              0 |             1
     genre_stat_1_prt_r188375637_2_prt_detective | detective     | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_fantastic | fantastic     | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_fantasy   | fantasy       | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_poetry    | poetry        | list          |              1 |
    (13 rows)
ПРИМЕЧАНИЕ
  • Для добавления новой партиции на одном уровне с партицией по умолчанию к последней необходимо применить операцию split.

  • При добавлении партиции с типом range ранг партиций, находящихся на одном уровне с ней, автоматически обновляется (см. столбец partitionrank выше).

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

 
Чтобы изменить имя партиции, необходимо использовать команду ALTER TABLE с выражением RENAME PARTITION. Следующий пример показывает переименование партиции с рангом 1 для родительской таблицы book, созданной выше (см. Партиционирование на основе числовых диапазонов (автоматически)).

  1. Проверьте текущее имя партиции:

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'book';

    Результат приведен ниже. Партиция с рангом 1 имеет полное табличное имя book_1_prt_2, где 2  — автоматичекси сгенерированный номер при создании:

     partitiontablename | partitionname | partitiontype | partitionlevel | partitionrank
    --------------------+---------------+---------------+----------------+---------------
     book_1_prt_2       |               | range         |              0 |             1
     book_1_prt_3       |               | range         |              0 |             2
     book_1_prt_4       |               | range         |              0 |             3
     book_1_prt_5       |               | range         |              0 |             4
     book_1_prt_6       |               | range         |              0 |             5
     book_1_prt_7       |               | range         |              0 |             6
     book_1_prt_8       |               | range         |              0 |             7
     book_1_prt_9       |               | range         |              0 |             8
     book_1_prt_10      |               | range         |              0 |             9
     book_1_prt_11      |               | range         |              0 |            10
     book_1_prt_other   | other         | range         |              0 |
    (11 rows)
  2. Переименуйте партицию, как указано ниже:

    ALTER TABLE book RENAME PARTITION FOR(rank(1)) TO year2013;

    Результат:

    ALTER TABLE
  3. Проверьте имя партиции повторно (используя запрос с шага 1). Имя партиции partitionname и имя соответствующей таблицы partitiontablename обновлены:

     partitiontablename  | partitionname | partitiontype | partitionlevel | partitionrank
    ---------------------+---------------+---------------+----------------+---------------
     book_1_prt_year2013 | year2013      | range         |              0 |             1
     book_1_prt_3        |               | range         |              0 |             2
     book_1_prt_4        |               | range         |              0 |             3
     book_1_prt_5        |               | range         |              0 |             4
     book_1_prt_6        |               | range         |              0 |             5
     book_1_prt_7        |               | range         |              0 |             6
     book_1_prt_8        |               | range         |              0 |             7
     book_1_prt_9        |               | range         |              0 |             8
     book_1_prt_10       |               | range         |              0 |             9
     book_1_prt_11       |               | range         |              0 |            10
     book_1_prt_other    | other         | range         |              0 |
    (11 rows)

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

ALTER TABLE book RENAME TO magazine;

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

SELECT
  partitiontablename,
  partitionname,
  partitiontype,
  partitionlevel,
  partitionrank
FROM pg_catalog.pg_partitions
WHERE schemaname = 'public'
AND   tablename  = 'magazine';

Результат:

   partitiontablename    | partitionname | partitiontype | partitionlevel | partitionrank
-------------------------+---------------+---------------+----------------+---------------
 magazine_1_prt_year2013 | year2013      | range         |              0 |             1
 magazine_1_prt_3        |               | range         |              0 |             2
 magazine_1_prt_4        |               | range         |              0 |             3
 magazine_1_prt_5        |               | range         |              0 |             4
 magazine_1_prt_6        |               | range         |              0 |             5
 magazine_1_prt_7        |               | range         |              0 |             6
 magazine_1_prt_8        |               | range         |              0 |             7
 magazine_1_prt_9        |               | range         |              0 |             8
 magazine_1_prt_10       |               | range         |              0 |             9
 magazine_1_prt_11       |               | range         |              0 |            10
 magazine_1_prt_other    | other         | range         |              0 |
(11 rows)
Добавление партиции по умолчанию

 
Чтобы добавить партицию по умолчанию в партиционированную таблицу, необходимо использовать команду ALTER TABLE с выражением ADD DEFAULT PARTITION. Следующий пример показывает добавление партиции по умолчанию в таблицу genre_stat, созданную выше (см. Добавление новой партиции):

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

    ALTER TABLE genre_stat ADD DEFAULT PARTITION other;

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

    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_other" for table "genre_stat"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_other_2_prt_poetry" for table "genre_stat_1_prt_other"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_other_2_prt_fantasy" for table "genre_stat_1_prt_other"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_other_2_prt_detective" for table "genre_stat_1_prt_other"
    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_other_2_prt_fantastic" for table "genre_stat_1_prt_other"
    ALTER TABLE
  2. Возможно также добавление партиций по умолчанию на других уровнях иерархии. Для этого необходимо указать полный путь к новой партиции при помощи выражений ALTER PARTITION — по одному выражению для каждого уровня партиционирования, предшествующего партиции по умолчанию. Следующий запрос создает партицию по умолчанию на втором уровне иерархии:

    ALTER TABLE genre_stat ALTER PARTITION FOR (RANK(1)) ADD DEFAULT PARTITION other;

    Результат:

    NOTICE:  CREATE TABLE will create partition "genre_stat_1_prt_r188375637_2_prt_other" for table "genre_stat_1_prt_r188375637"
    ALTER TABLE
  3. Для просмотра обновленной структуры партиционированной таблицы выполните запрос:

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'genre_stat' ORDER BY partitiontablename;

    Результат:

                 partitiontablename              | partitionname | partitiontype | partitionlevel | partitionrank
    ---------------------------------------------+---------------+---------------+----------------+---------------
     genre_stat_1_prt_1                          |               | range         |              0 |             2
     genre_stat_1_prt_1_2_prt_detective          | detective     | list          |              1 |
     genre_stat_1_prt_1_2_prt_fantasy            | fantasy       | list          |              1 |
     genre_stat_1_prt_1_2_prt_poetry             | poetry        | list          |              1 |
     genre_stat_1_prt_2                          |               | range         |              0 |             3
     genre_stat_1_prt_2_2_prt_detective          | detective     | list          |              1 |
     genre_stat_1_prt_2_2_prt_fantasy            | fantasy       | list          |              1 |
     genre_stat_1_prt_2_2_prt_poetry             | poetry        | list          |              1 |
     genre_stat_1_prt_other                      | other         | range         |              0 |
     genre_stat_1_prt_other_2_prt_detective      | detective     | list          |              1 |
     genre_stat_1_prt_other_2_prt_fantastic      | fantastic     | list          |              1 |
     genre_stat_1_prt_other_2_prt_fantasy        | fantasy       | list          |              1 |
     genre_stat_1_prt_other_2_prt_poetry         | poetry        | list          |              1 |
     genre_stat_1_prt_r188375637                 |               | range         |              0 |             1
     genre_stat_1_prt_r188375637_2_prt_detective | detective     | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_fantastic | fantastic     | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_fantasy   | fantasy       | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_other     | other         | list          |              1 |
     genre_stat_1_prt_r188375637_2_prt_poetry    | poetry        | list          |              1 |
    (19 rows)
Разделение партиции (split)

 
Для разделения партиции на две необходимо использовать команду ALTER TABLE с выражением SPLIT PARTITION. Следующий пример показывает разделение партиций в родительской таблице book_order, созданной выше (см. Партиционирование на основе диапазонов дат (автоматически)):

  1. Разделите партицию, хранящую данные за январь 2022 года:

    ALTER TABLE book_order SPLIT PARTITION FOR ('2022-01-01')
    AT ('2022-01-16')
    INTO (PARTITION jan1to15, PARTITION jan16to31);

    Результат приведен ниже. Значение, на основе которого производится деление (2022-01-16), попадает во вторую партицию. Поэтому первая партиция будет хранить данные с 1 по 15 января 2022 года, вторая партиция — с 16 по 31 января 2022 года.

    NOTICE:  CREATE TABLE will create partition "book_order_1_prt_jan1to15" for table "book_order"
    NOTICE:  CREATE TABLE will create partition "book_order_1_prt_jan16to31" for table "book_order"
    ALTER TABLE
  2. Операция split также применяется для добавления новых партиций на одном уровне с партициями по умолчанию. Попробуйте добавить новую партицию (для хранения данных за декабрь 2021 года) в таблицу book_order:

    ALTER TABLE book_order ADD PARTITION
    START ('2021-12-01')
    END ('2022-01-01');

    Команда возвращает следующую ошибку:

    ERROR:  cannot add RANGE partition to relation "book_order" with DEFAULT partition "other"
    HINT:  need to SPLIT partition "other"
  3. Разделите партицию по умолчанию other с помощью следующей команды:

    ALTER TABLE book_order SPLIT DEFAULT PARTITION
    START ('2021-12-01')
    END ('2022-01-01')
    INTO (PARTITION dec21, DEFAULT PARTITION);

    Результат приведен ниже:

    NOTICE:  exchanged partition "other" of relation "book_order" with relation "pg_temp_61865"
    NOTICE:  dropped partition "other" for relation "book_order"
    NOTICE:  CREATE TABLE will create partition "book_order_1_prt_dec21" for table "book_order"
    NOTICE:  CREATE TABLE will create partition "book_order_1_prt_other" for table "book_order"
    ALTER TABLE
  4. Для просмотра обновленной структуры партиционированной таблицы используйте следующий запрос:

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public' AND   tablename  = 'book_order';

    Результат:

         partitiontablename     | partitionname | partitiontype | partitionlevel | partitionrank
    ----------------------------+---------------+---------------+----------------+---------------
     book_order_1_prt_dec21     | dec21         | range         |              0 |             1
     book_order_1_prt_jan1to15  | jan1to15      | range         |              0 |             2
     book_order_1_prt_jan16to31 | jan16to31     | range         |              0 |             3
     book_order_1_prt_3         |               | range         |              0 |             4
     book_order_1_prt_4         |               | range         |              0 |             5
     book_order_1_prt_5         |               | range         |              0 |             6
     book_order_1_prt_6         |               | range         |              0 |             7
     book_order_1_prt_7         |               | range         |              0 |             8
     book_order_1_prt_8         |               | range         |              0 |             9
     book_order_1_prt_9         |               | range         |              0 |            10
     book_order_1_prt_10        |               | range         |              0 |            11
     book_order_1_prt_11        |               | range         |              0 |            12
     book_order_1_prt_12        |               | range         |              0 |            13
     book_order_1_prt_13        |               | range         |              0 |            14
     book_order_1_prt_other     | other         | range         |              0 |
    (15 rows)
ПРИМЕЧАНИЕ
  • Операция разделения может применяться только к партициям, расположенным на нижнем уровне иерархии.

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

Обмен партиции (exchange)

 
Операция обмена (exchange) позволяет заменять партицию на таблицу с аналогичной структурой. Эту таблицу принято называть staging-таблицей. После применения операции staging-таблица содержит данные партиции, а партиция — данные staging-таблицы. Этот функционал особенно полезен для загрузки данных, поскольку вставка данных непосредственно в сами партиции не рекомендуется в силу своей неэффективности. Обмен партиции также позволяет изменять тип хранения данных в существующих партициях.

Чтобы выполнить обмен партиции, необходимо использовать команду ALTER TABLE с выражением EXCHANGE PARTITION. Следующий пример показывает обмен партиции для таблицы book_order_manual, созданной выше (см. Партиционирование на основе диапазонов дат (вручную)):

  1. Просмотрите родительскую таблицу вместе со всеми партициями, используя команду psql \dt+:

    \dt+ book_order_manual*

    Результат:

                                           List of relations
     Schema |             Name              | Type  |  Owner  |   Storage   |  Size  | Description
    --------+-------------------------------+-------+---------+-------------+--------+-------------
     public | book_order_manual             | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_apr22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_aug22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_dec22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_feb22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jan22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jul22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jun22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_mar22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_may22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_nov22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_oct22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_other | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_sep22 | table | gpadmin | append only | 544 kB |
    (14 rows)
  2. Убедитесь, что партиция book_order_manual_1_prt_dec22 не содержит данных:

    SELECT * FROM book_order_manual_1_prt_dec22;

    Результат:

     id | book_id | client_id | book_count | order_date
    ----+---------+-----------+------------+------------
    (0 rows)
  3. Создайте новую таблицу с той же структурой, что у партиционированной таблицы book_order_manual. Выберите другой тип хранения данных (heap):

    CREATE TABLE dec22 (LIKE book_order_manual) WITH (appendoptimized=false);

    Результат:

    NOTICE:  table doesn't have 'DISTRIBUTED BY' clause, defaulting to distribution columns from LIKE table
    CREATE TABLE
  4. Добавьте данные в новую таблицу:

    INSERT INTO dec22(id, book_id, client_id, book_count, order_date)
    VALUES(1, 1, 1, 1, '2022-12-01');

    Результат:

    INSERT 0 1
  5. Замените партицию book_order_manual_1_prt_dec22 новой таблицей. Используйте выражение WITH VALIDATION для включения проверки того, что данные в таблице отвечают требованиям из спецификации партиции. Выражение WITHOUT VALIDATION может быть использовано для отключения такой проверки:

    ALTER TABLE book_order_manual EXCHANGE PARTITION FOR (DATE '2022-12-01')
    WITH TABLE dec22 WITH VALIDATION;

    Результат:

    NOTICE:  exchanged partition "dec22" of relation "book_order_manual" with relation "dec22"
    ALTER TABLE
  6. Проверьте, что партиция содержит данные (используя запрос с шага 2):

    adb=# SELECT * FROM book_order_manual_1_prt_dec22;
     id | book_id | client_id | book_count | order_date
    ----+---------+-----------+------------+------------
      1 |       1 |         1 |          1 | 2022-12-01
    (1 row)
  7. Убедитесь, что тип хранения данных в партиции также изменился (используя psql-команду с шага 1):

    adb=# \dt+ book_order_manual*
                                           List of relations
     Schema |             Name              | Type  |  Owner  |   Storage   |  Size  | Description
    --------+-------------------------------+-------+---------+-------------+--------+-------------
     public | book_order_manual             | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_apr22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_aug22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_dec22 | table | gpadmin | heap        | 32 kB  |
     public | book_order_manual_1_prt_feb22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jan22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jul22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_jun22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_mar22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_may22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_nov22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_oct22 | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_other | table | gpadmin | append only | 544 kB |
     public | book_order_manual_1_prt_sep22 | table | gpadmin | append only | 544 kB |
    (14 rows)

Возможность обмена для партиций по умолчанию (default partitions) изначально отключена. Но это поведение системы можно изменить с помощью установки серверного конфигурационного параметра gp_enable_exchange_default_partition в значение on. Это позволит использовать выражение EXCHANGE DEFAULT PARTITION в команде ALTER TABLE. При обмене партиций по умолчанию важно проверять, чтобы данные в staging-таблице были валидны исключительно для партиции по умолчанию и не подходили под требования какой-либо из других партиций.

ПРИМЕЧАНИЕ
  • Операцию обмена можно проводить только для партиций, расположенных на нижнем уровне иерархии.

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

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

  • При использовании выражений WITHOUT VALIDATION и EXCHANGE DEFAULT PARTITION следует убедиться, что данные в staging-таблице отвечают требованиям спецификации партиции. В противном случае запросы к партиционированной таблице могут начать возвращать неверные данные или привести к более серьезным системным сбоям.

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

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

 
Чтобы удалить все данные из партиции без удаления самой партиции, необходимо использовать команду ALTER TABLE с выражением TRUNCATE PARTITION. Следующий пример показывает удаление данных из партиции sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction, которые были добавлены выше:

  1. Проверьте наличие данных в партиции:

    SELECT * FROM sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;

    Результат:

     id |    date    | fiction | region
    ----+------------+---------+--------
      2 | 2022-01-02 | f       | asia
      1 | 2022-01-01 | f       | asia
    (2 rows)
  2. Удалите данные из партиции. Обратите внимание, что для таблиц с многоуровневым партиционированием необходимо указывать полный путь к дочерней партиции, используя выражения ALTER PARTITION для описания каждого уровня партиционирования, предшествующего искомой партиции:

    ALTER TABLE sales_manual
    ALTER PARTITION jan2022
    ALTER PARTITION asia
    TRUNCATE PARTITION fiction;

    Результат:

    NOTICE:  truncated partition "fiction" for partition "asia" of partition "jan2022" of relation "sales_manual"
    ALTER TABLE
  3. Убедитесь, что партиция более не содержит данных (используя запрос с шага 1):

    adb=# SELECT * FROM sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;
    id | date | fiction | region
    ----+------+---------+--------
    (0 rows)
ПРИМЕЧАНИЕ
При удалении данных из партиции, имеющей сабпартиции, данные из последних также автоматически удаляются.
Удаление партиции (drop)

 
Чтобы удалить партицию, необходимо использовать команду ALTER TABLE с выражением DROP PARTITION. Следующий пример показывает удаление партиции Year2013 из таблицы book_manual, созданной выше (см. Партиционирование на основе числовых диапазонов (вручную)):

  1. Проверьте существование партиции:

    SELECT
      partitiontablename,
      partitionname,
      partitiontype,
      partitionlevel,
      partitionrank
    FROM pg_catalog.pg_partitions
    WHERE schemaname = 'public'
    AND   tablename  = 'book_manual';

    Результат:

         partitiontablename     | partitionname | partitiontype | partitionlevel | partitionrank
    ----------------------------+---------------+---------------+----------------+---------------
     book_manual_1_prt_year2013 | year2013      | range         |              0 |             1
     book_manual_1_prt_year2014 | year2014      | range         |              0 |             2
     book_manual_1_prt_year2015 | year2015      | range         |              0 |             3
     book_manual_1_prt_year2016 | year2016      | range         |              0 |             4
     book_manual_1_prt_year2017 | year2017      | range         |              0 |             5
     book_manual_1_prt_year2018 | year2018      | range         |              0 |             6
     book_manual_1_prt_year2019 | year2019      | range         |              0 |             7
     book_manual_1_prt_year2020 | year2020      | range         |              0 |             8
     book_manual_1_prt_year2021 | year2021      | range         |              0 |             9
     book_manual_1_prt_year2022 | year2022      | range         |              0 |             10
     book_manual_1_prt_other    | other         | range         |              0 |
    (11 rows)
  2. Удалите партицию:

    ALTER TABLE book_manual DROP PARTITION year2013;

    Результат:

    ALTER TABLE
  3. Убедитесь, что партиция более не существует (используя запрос с шага 1):

         partitiontablename     | partitionname | partitiontype | partitionlevel | partitionrank
    ----------------------------+---------------+---------------+----------------+---------------
     book_manual_1_prt_year2014 | year2014      | range         |              0 |             1
     book_manual_1_prt_year2015 | year2015      | range         |              0 |             2
     book_manual_1_prt_year2016 | year2016      | range         |              0 |             3
     book_manual_1_prt_year2017 | year2017      | range         |              0 |             4
     book_manual_1_prt_year2018 | year2018      | range         |              0 |             5
     book_manual_1_prt_year2019 | year2019      | range         |              0 |             6
     book_manual_1_prt_year2020 | year2020      | range         |              0 |             7
     book_manual_1_prt_year2021 | year2021      | range         |              0 |             8
     book_manual_1_prt_year2022 | year2022      | range         |              0 |             9
     book_manual_1_prt_other    | other         | range         |              0 |
    (10 rows)
ПРИМЕЧАНИЕ
  • При удалении партиции, имеющей сабпартиции, последние автоматически удаляются (вместе со всеми данными).

  • При удалении партиции с типом range ранг партиций, находящихся на одном уровне с ней, автоматически обновляется (см. столбец partitionrank выше).

Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней