Партиционирование
Обзор
Партиционирование (partitioning) — это способ повышения производительности запросов за счет логического разбиения больших таблиц (например, таблиц фактов) на небольшие части, называемые партициями (partition). Партиционирование позволяет оптимизаторам запросов сканировать ограниченное число строк в таблице вместо чтения всего содержимого таблицы.
Принадлежность каждой новой записи таблицы к той или иной партиции определяется на основе значения ключа партиционирования (partition key) — столбца (или набора столбцов в случае многоуровневого партиционирования), который выбирается при создании партиционированной таблицы.
Партиционирование выполняется на логическом уровне. В отличие от распределения данных, партиционирование не делит таблицу физически. Как партиционированные, так и непартиционированные таблицы распределяются между сегментами кластера.
Несмотря на преимущества партиционирования, далеко не все таблицы подходят для его применения. Также следует учитывать: чем больше партиций создается, тем медленнее может стать управление кластером в дальнейшем. Ниже перечислены условия, при выполнении которых партиционирование может существенно повысить производительность запросов. Используйте партиционирование, если все или большинство описанных условий применимы к базе данных:
-
Существует большая таблица фактов. Таблицы фактов с миллионами строк являются хорошими кандидатами для применения партиционирования. Партиционирование небольших таблиц с тысячами строк и меньше, напротив, малоэффективно.
-
Текущая скорость выполнения запросов неудовлетворительна. Применяйте партиционирование только к тем таблицам, запросы к которым выполняются значительно медленнее, чем требуется. Это общая рекомендация перед использованием любых методов оптимизации.
-
Существует столбец, на основе которого можно разбить таблицу на примерно одинаковые части. Старайтесь выбирать ключ партиционирования таким образом, чтобы количество строк в каждой партиции было приблизительно одинаковым. Чем равномернее таблица будет разбита на небольшие части, тем больше преимуществ можно получить. Например, разбив таблицу на 10 одинаковых партиций, можно добиться увеличения скорости выполнения запросов в 10 раз по сравнению с непартиционированной таблицей (при условии, что ключ партиционирования используется в предикатах запросов — см. пункт ниже).
-
Большинство запросов, требующих ускорения, используют ключ партиционирования в предикатах. Партиционирование приносит пользу только в случае, если оптимизаторы запросов могут отобрать нужные партиции на основе предикатов, используемых в запросах. Запросы, сканирующие все партиции, могут работать даже медленнее, чем до применения партиционирования. Убедитесь, что планы выполнения запросов содержат partition elimination (сканирование ограниченного числа партиций).
-
Существуют бизнес-требования к хранению исторических данных. Партиционирование отлично справляется с задачей хранения данных за ограниченный период времени. Например, если в базе данных требуется хранить информацию за последние 12 месяцев, можно легко удалять партиции с устаревшими данными и загружать актуальные данные в новые партиции.
Создание партиционированной таблицы
Синтаксис
ADB 7 поддерживает большинство синтаксических конструкций для создания и управления партициями из ADB 6 — так называемый классический (classic) синтаксис партиционирования. Дополнительно в ADB 7 реализована поддержка обновленного (modern) синтаксиса партиционирования, соответствующего декларативному синтаксису, принятому в PostgreSQL. Классический синтаксис поддерживается для обратной совместимости с предыдущими версиями ADB. Из двух альтернативных форм синтаксиса вы можете выбрать наиболее подходящую для ваших нужд. Однако не рекомендуется смешивать оба синтаксиса при работе с одной партиционированной таблицей. Все дальнейшие примеры в статье приведены для обеих форм синтаксиса.
Для создания партиционированной таблицы используется команда 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 на нескольких уровнях. Оно позволяет разделять партиции первого уровня на сабпартиции (subpartition) второго уровня, а их в свою очередь — на сабпартиции третьего уровня и так далее. Выражение
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 для сабпартиций) сразу после спецификации партиции (или сабпартиции). Этот тип партиций предназначен для хранения данных, которые не попадают в указанный диапазон (или список) значений.
Для создания верхнеуровневой партиционированной таблицы используется команда CREATE TABLE с выражением PARTITION BY, после которого указывается ключ партиционирования. Далее необходимо описать каждую партицию отдельно с помощью команды CREATE TABLE … PARTITION OF … FOR VALUES … [PARTITION BY …]. Синтаксис команд зависит от типа партиционирования: range, list, hash или комбинация типов (multi-level):
-
Партиционирование на основе диапазонов (range). Партиционирование с типом range осуществляет разделение данных на основе числовых или временных (date/timestamp) диапазонов. При создании верхнеуровневой партиционированной таблицы используйте выражение
PARTITION BY RANGE, после чего выберите ключ партиционирования. При добавлении каждой партиции используйте выражениеFOR VALUES FROM (<start_value>) TO (<end_value>)для определения возможного диапазона значений для этой партиции. -
Партиционирование на основе списков значений (list). Партиционирование с типом list выполняет разделение данных на основе предопределенного списка значений ключа партиционирования. При создании верхнеуровневой партиционированной таблицы используйте выражение
PARTITION BY LIST, после чего выберите ключ партиционирования. При добавлении каждой партиции используйте выражениеFOR VALUES IN (<list_values>)для определения возможного списка значений для этой партиции. -
Хеш-партиционирование. Хеш-партиционирование доступно начиная с ADB 7 только в случае использования обновленного (modern) синтаксиса. Каждая партиция будет содержать строки, для которых в результате деления хеша от значения ключа партиционирования на заданное в спецификации партиции число-делитель формируется заданный в этой же спецификации остаток. При создании верхнеуровневой партиционированной таблицы используйте выражение
PARTITION BY HASH, после чего выберите ключ партиционирования. При добавлении каждой партиции используйте выражениеFOR VALUES WITH (MODULUS <modulus_value>, REMAINDER <remainder_value>)для определения делителя и остатка соответственно. -
Многоуровневое партиционирование (multi-level). Партиционирование с типом multi-level — это комбинация партиционирования различных видов на нескольких уровнях. Оно позволяет разделять партиции первого уровня на сабпартиции (subpartition) второго уровня, а их в свою очередь — на сабпартиции третьего уровня и так далее. Выражение
PARTITION BY(RANGE,LISTилиHASH) используется для определения ключа партиционирования на каждом уровне иерархии.
Для всех видов партиционирования возможно указание партиции по умолчанию (default partition) с использованием выражения DEFAULT сразу после спецификации партиции. Этот тип партиций предназначен для хранения данных, которые не попадают в указанный диапазон (или список) значений.
|
ВАЖНО
|
Примеры
Следующий пример показывает создание двенадцати партиций на основе диапазона дат — по одной партиции на каждый месяц 2022 года. Также создается партиция по умолчанию.
CREATE TABLE book_order
(id INT,
book_id INT,
client_id INT,
book_count SMALLINT,
order_date DATE
)
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);
Следующий пример показывает создание десяти партиций на основе числового диапазона — по одной партиции на каждый год с 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);
Следующий пример показывает создание таблицы book_order_manual со структурой полностью идентичной таблице book_order, созданной выше — за исключением того, что каждая партиция теперь описывается отдельно.
CREATE TABLE book_order_manual
(id INT,
book_id INT,
client_id INT,
book_count SMALLINT,
order_date DATE
)
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);
Следующий пример показывает создание таблицы 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);
Следующий пример показывает создание двух партиций на основе значений столбца 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);
Следующий пример демонстрирует трехуровневое партиционирование. Команда добавляет три партиции с типом 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
);
Следующий пример показывает создание таблицы 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
)
)
);
Следующий пример показывает создание двенадцати партиций на основе диапазона дат — по одной партиции на каждый месяц 2022 года. Также создается партиция по умолчанию.
CREATE TABLE book_order_manual
(id INT,
book_id INT,
client_id INT,
book_count SMALLINT,
order_date DATE
)
WITH (appendoptimized=true, orientation=row, compresstype=ZLIB, compresslevel=5)
DISTRIBUTED BY(id)
PARTITION BY RANGE(order_date);
CREATE TABLE book_order_manual_1_prt_jan22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-01-01') TO ('2022-02-01');
CREATE TABLE book_order_manual_1_prt_feb22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-02-01') TO ('2022-03-01');
CREATE TABLE book_order_manual_1_prt_mar22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-03-01') TO ('2022-04-01');
CREATE TABLE book_order_manual_1_prt_apr22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-04-01') TO ('2022-05-01');
CREATE TABLE book_order_manual_1_prt_may22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-05-01') TO ('2022-06-01');
CREATE TABLE book_order_manual_1_prt_jun22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-06-01') TO ('2022-07-01');
CREATE TABLE book_order_manual_1_prt_jul22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-07-01') TO ('2022-08-01');
CREATE TABLE book_order_manual_1_prt_aug22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-08-01') TO ('2022-09-01');
CREATE TABLE book_order_manual_1_prt_sep22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-09-01') TO ('2022-10-01');
CREATE TABLE book_order_manual_1_prt_oct22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-10-01') TO ('2022-11-01');
CREATE TABLE book_order_manual_1_prt_nov22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-11-01') TO ('2022-12-01');
CREATE TABLE book_order_manual_1_prt_dec22 PARTITION OF book_order_manual FOR VALUES FROM ('2022-12-01') TO ('2023-01-01');
CREATE TABLE book_order_manual_1_prt_other PARTITION OF book_order_manual DEFAULT;
Следующий пример показывает создание десяти партиций на основе числового диапазона — по одной партиции на каждый год с 2013 по 2022. Также создается партиция по умолчанию.
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);
CREATE TABLE book_manual_1_prt_year2013 PARTITION OF book_manual FOR VALUES FROM (2013) TO (2014);
CREATE TABLE book_manual_1_prt_year2014 PARTITION OF book_manual FOR VALUES FROM (2014) TO (2015);
CREATE TABLE book_manual_1_prt_year2015 PARTITION OF book_manual FOR VALUES FROM (2015) TO (2016);
CREATE TABLE book_manual_1_prt_year2016 PARTITION OF book_manual FOR VALUES FROM (2016) TO (2017);
CREATE TABLE book_manual_1_prt_year2017 PARTITION OF book_manual FOR VALUES FROM (2017) TO (2018);
CREATE TABLE book_manual_1_prt_year2018 PARTITION OF book_manual FOR VALUES FROM (2018) TO (2019);
CREATE TABLE book_manual_1_prt_year2019 PARTITION OF book_manual FOR VALUES FROM (2019) TO (2020);
CREATE TABLE book_manual_1_prt_year2020 PARTITION OF book_manual FOR VALUES FROM (2020) TO (2021);
CREATE TABLE book_manual_1_prt_year2021 PARTITION OF book_manual FOR VALUES FROM (2021) TO (2022);
CREATE TABLE book_manual_1_prt_year2022 PARTITION OF book_manual FOR VALUES FROM (2022) TO (2023);
CREATE TABLE book_manual_1_prt_other PARTITION OF book_manual DEFAULT;
Следующий пример показывает создание двух партиций на основе значений столбца gender. Также добавляется партиция по умолчанию.
CREATE TABLE client (id INT, name TEXT, gender CHAR(1))
DISTRIBUTED BY (id)
PARTITION BY LIST (gender);
CREATE TABLE client_1_prt_girls PARTITION OF client FOR VALUES IN('F');
CREATE TABLE client_1_prt_boys PARTITION OF client FOR VALUES IN('M');
CREATE TABLE client_1_prt_other PARTITION OF client DEFAULT;
Следующий пример демонстрирует трехуровневое партиционирование. Команда добавляет три партиции с типом range на первом уровне иерархии, затем разделяет их на три сабпартиции на втором уровне — в зависимости от значений столбца region. Эти сабпартиции, в свою очередь, разделяются на три сабпартиции на третьем уровне — на основе значений столбца fiction. На каждом уровне иерархии добавляется партиция/сабпартиция по умолчанию.
CREATE TABLE sales_manual
(id INT,
date DATE,
fiction CHAR(1),
region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (date);
CREATE TABLE sales_manual_1_prt_other_dt PARTITION OF sales_manual DEFAULT PARTITION BY LIST (region);
CREATE TABLE sales_manual_1_prt_jan2022 PARTITION OF sales_manual FOR VALUES FROM ('2022-01-01') TO ('2022-02-01') PARTITION BY LIST (region);
CREATE TABLE sales_manual_1_prt_feb2022 PARTITION OF sales_manual FOR VALUES FROM ('2022-02-01') TO ('2022-03-01') PARTITION BY LIST (region);
CREATE TABLE sales_manual_1_prt_mar2022 PARTITION OF sales_manual FOR VALUES FROM ('2022-03-01') TO ('2022-04-01') PARTITION BY LIST (region);
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_other_rg PARTITION OF sales_manual_1_prt_other_dt DEFAULT PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_rus PARTITION OF sales_manual_1_prt_other_dt FOR VALUES IN('rus') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_asia PARTITION OF sales_manual_1_prt_other_dt FOR VALUES IN('asia') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_europe PARTITION OF sales_manual_1_prt_other_dt FOR VALUES IN('europe') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_other_rg PARTITION OF sales_manual_1_prt_jan2022 DEFAULT PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_rus PARTITION OF sales_manual_1_prt_jan2022 FOR VALUES IN('rus') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_asia PARTITION OF sales_manual_1_prt_jan2022 FOR VALUES IN('asia') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_europe PARTITION OF sales_manual_1_prt_jan2022 FOR VALUES IN('europe') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_other_rg PARTITION OF sales_manual_1_prt_feb2022 DEFAULT PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_rus PARTITION OF sales_manual_1_prt_feb2022 FOR VALUES IN('rus') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_asia PARTITION OF sales_manual_1_prt_feb2022 FOR VALUES IN('asia') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_europe PARTITION OF sales_manual_1_prt_feb2022 FOR VALUES IN('europe') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_other_rg PARTITION OF sales_manual_1_prt_mar2022 DEFAULT PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_rus PARTITION OF sales_manual_1_prt_mar2022 FOR VALUES IN('rus') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_asia PARTITION OF sales_manual_1_prt_mar2022 FOR VALUES IN('asia') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_europe PARTITION OF sales_manual_1_prt_mar2022 FOR VALUES IN('europe') PARTITION BY LIST (fiction);
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp PARTITION OF sales_manual_1_prt_other_dt_2_prt_other_rg DEFAULT;
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_other_rg FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_non_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_other_rg FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_rus_3_prt_other_tp PARTITION OF sales_manual_1_prt_other_dt_2_prt_rus DEFAULT;
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_rus_3_prt_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_rus FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_rus_3_prt_non_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_rus FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_asia_3_prt_other_tp PARTITION OF sales_manual_1_prt_other_dt_2_prt_asia DEFAULT;
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_asia_3_prt_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_asia FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_asia_3_prt_non_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_asia FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_europe_3_prt_other_tp PARTITION OF sales_manual_1_prt_other_dt_2_prt_europe DEFAULT;
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_europe_3_prt_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_europe FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_other_dt_2_prt_europe_3_prt_non_fiction PARTITION OF sales_manual_1_prt_other_dt_2_prt_europe FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_other_tp PARTITION OF sales_manual_1_prt_jan2022_2_prt_other_rg DEFAULT;
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_other_rg FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_non_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_other_rg FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_rus_3_prt_other_tp PARTITION OF sales_manual_1_prt_jan2022_2_prt_rus DEFAULT;
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_rus_3_prt_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_rus FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_rus_3_prt_non_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_rus FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_asia_3_prt_other_tp PARTITION OF sales_manual_1_prt_jan2022_2_prt_asia DEFAULT;
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_asia FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_asia_3_prt_non_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_asia FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_europe_3_prt_other_tp PARTITION OF sales_manual_1_prt_jan2022_2_prt_europe DEFAULT;
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_europe_3_prt_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_europe FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_jan2022_2_prt_europe_3_prt_non_fiction PARTITION OF sales_manual_1_prt_jan2022_2_prt_europe FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_other_tp PARTITION OF sales_manual_1_prt_feb2022_2_prt_other_rg DEFAULT;
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_other_rg FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_non_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_other_rg FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_rus_3_prt_other_tp PARTITION OF sales_manual_1_prt_feb2022_2_prt_rus DEFAULT;
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_rus_3_prt_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_rus FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_rus_3_prt_non_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_rus FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_asia_3_prt_other_tp PARTITION OF sales_manual_1_prt_feb2022_2_prt_asia DEFAULT;
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_asia_3_prt_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_asia FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_asia_3_prt_non_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_asia FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_europe_3_prt_other_tp PARTITION OF sales_manual_1_prt_feb2022_2_prt_europe DEFAULT;
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_europe_3_prt_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_europe FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_feb2022_2_prt_europe_3_prt_non_fiction PARTITION OF sales_manual_1_prt_feb2022_2_prt_europe FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_other_tp PARTITION OF sales_manual_1_prt_mar2022_2_prt_other_rg DEFAULT;
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_other_rg FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_non_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_other_rg FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_rus_3_prt_other_tp PARTITION OF sales_manual_1_prt_mar2022_2_prt_rus DEFAULT;
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_rus_3_prt_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_rus FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_rus_3_prt_non_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_rus FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_asia_3_prt_other_tp PARTITION OF sales_manual_1_prt_mar2022_2_prt_asia DEFAULT;
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_asia_3_prt_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_asia FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_asia_3_prt_non_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_asia FOR VALUES IN('n');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_europe_3_prt_other_tp PARTITION OF sales_manual_1_prt_mar2022_2_prt_europe DEFAULT;
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_europe_3_prt_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_europe FOR VALUES IN('f');
CREATE TABLE sales_manual_1_prt_mar2022_2_prt_europe_3_prt_non_fiction PARTITION OF sales_manual_1_prt_mar2022_2_prt_europe FOR VALUES IN('n');
В следующем примере создается партиционированная таблица, использующая в качестве ключа партиционирования хеш от значения текстового столбца. Каждая партиция будет содержать строки, для которых при делении хеша на выбранное число по модулю (MODULUS) формируется заданный остаток (REMAINDER).
CREATE TABLE hash_test (a int, b int, c text) PARTITION BY HASH(c);
CREATE TABLE hash_test_1_prt_option1 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 0);
CREATE TABLE hash_test_1_prt_option2 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 1);
CREATE TABLE hash_test_1_prt_option3 PARTITION OF hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 2);
Внутренние детали партиционирования
Независимо от типа партиционирования, Greengage DB создает верхнеуровневую (родительскую) таблицу с одним или несколькими уровнями дочерних таблиц. Между родительской и дочерними таблицами устанавливается отношение наследования, схожее по функциональности с INHERITS в PostgreSQL. Если при использовании psql запустить команды \dt или \dt+ для вывода списка отношений БД, можно убедиться, что все партиции и сабпартиции отобразятся в списке подобно обычным таблицам. Обратите внимание, что начиная с ADB 7 корневые партиционированные таблицы, а также партиции промежуточных уровней (не являющиеся листовыми) отображаются с типом partitioned table, а листовые партиции — с типом table. В ADB 6 все партиции выводятся с общим типом — table.
Родительские таблицы всегда пусты. Данные хранятся в дочерних партициях, при использовании многоуровневого партиционирования — в сабпартициях самого нижнего уровня (leaf-партициях).
Для проверки того, какие данные могут быть добавлены в партиции, Greengage DB использует ограничения CHECK. Строки, не удовлетворяющие спецификациям ни одной нижнеуровневой партиции, отклоняются, и загрузка данных завершается с ошибкой (при условии, что не определена партиция по умолчанию). Оптимизатор запросов также использует ограничения CHECK для сканирования партиций, удовлетворяющих условиям предиката.
Чтобы добавить данные в партиционированную таблицу, необходимо использовать имя родительской таблицы, созданной с помощью команды CREATE TABLE, либо указать полное имя нужной партиции нижнего уровня. В партиции промежуточных уровней допускается добавлять данные начиная с ADB 7.
Например, можно добавить данные в таблицу 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');
Начиная с ADB 7 можно добавлять данные и в партицию, не являющуюся нижнеуровневой (например, sales_manual_1_prt_jan2022):
INSERT INTO sales_manual_1_prt_jan2022(id, date, fiction, region) VALUES(3, date '2022-01-03', 'f', 'asia');
В ADB 6 такой запрос завершается ошибкой:
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: new row for relation "sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction" violates partition constraint (seg0 10.92.41.82:10000 pid=4735) DETAIL: Failing row contains (4, 2022-01-02, n, asia).
Для выбора данных из партиционированной таблицы можно обратиться как к родительской таблице, так и к дочерней сабпартиции, содержащей данные:
SELECT * FROM sales_manual;
SELECT * FROM sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;
Оба запроса возвращают одинаковый результат:
id | date | fiction | region ----+------------+---------+-------- 3 | 2022-01-03 | f | asia 1 | 2022-01-01 | f | asia 2 | 2022-01-02 | f | asia (3 rows)
Партиционирование существующей таблицы
Партиционировать таблицы можно только при их создании. Чтобы разделить на партиции существующую таблицу, необходимо создать новую партиционированную таблицу и загрузить в нее данные из исходной. Пример приведен ниже:
-
Создайте исходную таблицу без партиционирования:
CREATE TABLE genre (id INT, name TEXT NOT NULL, fiction CHAR(1) NOT NULL) DISTRIBUTED BY (id); -
Создайте партиционированную таблицу с аналогичной структурой — с помощью выражения
LIKE:
CREATE TABLE genre_partitioned (LIKE genre)
PARTITION BY LIST (fiction)
(PARTITION fiction VALUES ('F'),
PARTITION non_fiction VALUES ('N'),
DEFAULT PARTITION other
);
CREATE TABLE genre_partitioned (LIKE genre)
PARTITION BY LIST (fiction);
CREATE TABLE genre_partitioned_1_prt_other PARTITION OF genre_partitioned DEFAULT;
CREATE TABLE genre_partitioned_1_prt_fiction PARTITION OF genre_partitioned FOR VALUES IN('F');
CREATE TABLE genre_partitioned_1_prt_non_fiction PARTITION OF genre_partitioned FOR VALUES IN('N');
|
ВАЖНО
При создании таблицы с помощью выражения |
-
Загрузите данные из исходной таблицы в партиционированную:
INSERT INTO genre_partitioned SELECT * FROM genre; -
Удалите исходную таблицу:
DROP TABLE genre; -
Назначьте имя удаленной таблицы партиционированной таблице:
ALTER TABLE genre_partitioned RENAME TO genre; -
Переопределите все необходимые разрешения для партиционированной таблицы:
GRANT ALL PRIVILEGES ON genre TO <role_name>;
Просмотр партиционированных таблиц
Для просмотра существующих партиционированных таблиц в текущей БД можно использовать следующие способы:
-
Чтобы вывести все партиционированные таблицы, можно выполнить следующий запрос к таблице pg_partitioned_table:
SELECT pg_class.relname AS partition_table_name, pg_attribute.attname AS column_in_partition_key, class2.relname AS default_partition, pg_partitioned_table.partstrat AS partition_type FROM pg_partitioned_table INNER JOIN pg_class ON pg_class.oid = pg_partitioned_table.partrelid INNER JOIN pg_attribute ON pg_attribute.attnum IN (SELECT unnest(pg_partitioned_table.partattrs)) AND pg_attribute.attrelid = pg_class.oid LEFT JOIN pg_class class2 ON class2.oid = pg_partitioned_table.partdefid ORDER BY pg_class.relname;Эта команда возвращает следующие данные:
-
partition_table_name— полное имя таблицы, используемое для обращения к партиции напрямую в DML-командах (таких какINSERT,COPY,SELECTи так далее). -
column_in_partition_key— имя столбца, используемого в качестве ключа партиционирования. В случае, если ключ партиционирования включает несколько столбцов, в результатах запроса будет по одной строке на каждый столбец. -
default_partition— название партиции, используемой по умолчанию (при ее наличии). -
partition_type— тип партиционирования:-
h— hash; -
l— list; -
r— range.
-
Обратите внимание, что листовые партиции в результатах запроса не выводятся.
Результатpartition_table_name | column_in_partition_key | default_partition | partition_type --------------------------------------------+-------------------------+-----------------------------------------------------------+---------------- book | public_year | book_1_prt_other | r book_manual | public_year | book_manual_1_prt_other | r book_order | order_date | book_order_1_prt_other | r book_order_manual | order_date | book_order_manual_1_prt_other | r client | gender | client_1_prt_other | l sales | date | sales_1_prt_other_dt | r sales_1_prt_2 | region | sales_1_prt_2_2_prt_other_rg | l sales_1_prt_2_2_prt_asia | fiction | sales_1_prt_2_2_prt_asia_3_prt_other_tp | l sales_1_prt_2_2_prt_europe | fiction | sales_1_prt_2_2_prt_europe_3_prt_other_tp | l sales_1_prt_2_2_prt_other_rg | fiction | sales_1_prt_2_2_prt_other_rg_3_prt_other_tp | l sales_1_prt_2_2_prt_rus | fiction | sales_1_prt_2_2_prt_rus_3_prt_other_tp | l sales_1_prt_3 | region | sales_1_prt_3_2_prt_other_rg | l sales_1_prt_3_2_prt_asia | fiction | sales_1_prt_3_2_prt_asia_3_prt_other_tp | l sales_1_prt_3_2_prt_europe | fiction | sales_1_prt_3_2_prt_europe_3_prt_other_tp | l sales_1_prt_3_2_prt_other_rg | fiction | sales_1_prt_3_2_prt_other_rg_3_prt_other_tp | l sales_1_prt_3_2_prt_rus | fiction | sales_1_prt_3_2_prt_rus_3_prt_other_tp | l sales_1_prt_4 | region | sales_1_prt_4_2_prt_other_rg | l sales_1_prt_4_2_prt_asia | fiction | sales_1_prt_4_2_prt_asia_3_prt_other_tp | l sales_1_prt_4_2_prt_europe | fiction | sales_1_prt_4_2_prt_europe_3_prt_other_tp | l sales_1_prt_4_2_prt_other_rg | fiction | sales_1_prt_4_2_prt_other_rg_3_prt_other_tp | l sales_1_prt_4_2_prt_rus | fiction | sales_1_prt_4_2_prt_rus_3_prt_other_tp | l sales_1_prt_other_dt | region | sales_1_prt_other_dt_2_prt_other_rg | l sales_1_prt_other_dt_2_prt_asia | fiction | sales_1_prt_other_dt_2_prt_asia_3_prt_other_tp | l sales_1_prt_other_dt_2_prt_europe | fiction | sales_1_prt_other_dt_2_prt_europe_3_prt_other_tp | l sales_1_prt_other_dt_2_prt_other_rg | fiction | sales_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp | l sales_1_prt_other_dt_2_prt_rus | fiction | sales_1_prt_other_dt_2_prt_rus_3_prt_other_tp | l sales_manual | date | sales_manual_1_prt_other_dt | r sales_manual_1_prt_feb2022 | region | sales_manual_1_prt_feb2022_2_prt_other_rg | l sales_manual_1_prt_feb2022_2_prt_asia | fiction | sales_manual_1_prt_feb2022_2_prt_asia_3_prt_other_tp | l sales_manual_1_prt_feb2022_2_prt_europe | fiction | sales_manual_1_prt_feb2022_2_prt_europe_3_prt_other_tp | l sales_manual_1_prt_feb2022_2_prt_other_rg | fiction | sales_manual_1_prt_feb2022_2_prt_other_rg_3_prt_other_tp | l sales_manual_1_prt_feb2022_2_prt_rus | fiction | sales_manual_1_prt_feb2022_2_prt_rus_3_prt_other_tp | l sales_manual_1_prt_jan2022 | region | sales_manual_1_prt_jan2022_2_prt_other_rg | l sales_manual_1_prt_jan2022_2_prt_asia | fiction | sales_manual_1_prt_jan2022_2_prt_asia_3_prt_other_tp | l sales_manual_1_prt_jan2022_2_prt_europe | fiction | sales_manual_1_prt_jan2022_2_prt_europe_3_prt_other_tp | l sales_manual_1_prt_jan2022_2_prt_other_rg | fiction | sales_manual_1_prt_jan2022_2_prt_other_rg_3_prt_other_tp | l sales_manual_1_prt_jan2022_2_prt_rus | fiction | sales_manual_1_prt_jan2022_2_prt_rus_3_prt_other_tp | l sales_manual_1_prt_mar2022 | region | sales_manual_1_prt_mar2022_2_prt_other_rg | l sales_manual_1_prt_mar2022_2_prt_asia | fiction | sales_manual_1_prt_mar2022_2_prt_asia_3_prt_other_tp | l sales_manual_1_prt_mar2022_2_prt_europe | fiction | sales_manual_1_prt_mar2022_2_prt_europe_3_prt_other_tp | l sales_manual_1_prt_mar2022_2_prt_other_rg | fiction | sales_manual_1_prt_mar2022_2_prt_other_rg_3_prt_other_tp | l sales_manual_1_prt_mar2022_2_prt_rus | fiction | sales_manual_1_prt_mar2022_2_prt_rus_3_prt_other_tp | l sales_manual_1_prt_other_dt | region | sales_manual_1_prt_other_dt_2_prt_other_rg | l sales_manual_1_prt_other_dt_2_prt_asia | fiction | sales_manual_1_prt_other_dt_2_prt_asia_3_prt_other_tp | l sales_manual_1_prt_other_dt_2_prt_europe | fiction | sales_manual_1_prt_other_dt_2_prt_europe_3_prt_other_tp | l sales_manual_1_prt_other_dt_2_prt_other_rg | fiction | sales_manual_1_prt_other_dt_2_prt_other_rg_3_prt_other_tp | l sales_manual_1_prt_other_dt_2_prt_rus | fiction | sales_manual_1_prt_other_dt_2_prt_rus_3_prt_other_tp | l (47 rows)
-
-
Чтобы вывести структуру партиционированной таблицы со всеми партициями, можно вызвать функцию
pg_partition_tree. В качестве аргумента<partition_table_name>допускается указать имя как родительской таблицы, так и любой ее партиции — структура будет показана для всех нижележащих уровней в иерархии:SELECT * from pg_partition_tree('<partition_table_name>');Эта команда возвращает следующие данные:
-
relid— полное имя таблицы, используемое для обращения к партиции напрямую в DML-командах (таких какINSERT,COPY,SELECTи так далее). -
parentrelid— имя родительской таблицы. -
isleaf— признак листовой партиции:-
t— да; -
f— нет.
-
-
level— уровень партиции в иерархии. Принимает значение0для таблицы, указанной в качестве аргумента функции<partition_table_name>(даже если она не является корневой партиционированной таблицей),1— для ее сабпартиций,2— для их сабпартиций и т.д.
Пример для верхнеуровневой таблицыSELECT * FROM pg_partition_tree('book');Результат:
relid | parentrelid | isleaf | level ------------------+-------------+--------+------- book | | f | 0 book_1_prt_other | book | t | 1 book_1_prt_2 | book | t | 1 book_1_prt_3 | book | t | 1 book_1_prt_4 | book | t | 1 book_1_prt_5 | book | t | 1 book_1_prt_6 | book | t | 1 book_1_prt_7 | book | t | 1 book_1_prt_8 | book | t | 1 book_1_prt_9 | book | t | 1 book_1_prt_10 | book | t | 1 book_1_prt_11 | book | t | 1 (12 rows)
Пример для партиции промежуточного уровняSELECT * FROM pg_partition_tree('sales_1_prt_4_2_prt_asia');Результат:
relid | parentrelid | isleaf | level --------------------------------------------+--------------------------+--------+------- sales_1_prt_4_2_prt_asia | sales_1_prt_4 | f | 0 sales_1_prt_4_2_prt_asia_3_prt_other_tp | sales_1_prt_4_2_prt_asia | t | 1 sales_1_prt_4_2_prt_asia_3_prt_fiction | sales_1_prt_4_2_prt_asia | t | 1 sales_1_prt_4_2_prt_asia_3_prt_non_fiction | sales_1_prt_4_2_prt_asia | t | 1 (4 rows)
-
-
Чтобы вывести все таблицы, являющиеся родительскими в иерархии партиционирования по отношению к выбранной партиции, можно вызвать функцию
pg_partition_ancestors. В качестве аргумента<partition_table_name>необходимо указать имя партиции:SELECT * from pg_partition_ancestors('<partition_table_name>');Эта команда возвращает один столбец
relid, показывающий имена вышестоящих таблиц в иерархии партиционирования. Выбранная партиция также попадает в вывод.Пример:
SELECT * FROM pg_partition_ancestors(' sales_1_prt_4_2_prt_asia');Результат:
relid -------------------------- sales_1_prt_4_2_prt_asia sales_1_prt_4 sales (3 rows)
-
Чтобы вывести таблицу, являющуюся верхнеуровневой (корневой) в иерархии партиционирования по отношению к выбранной партиции, можно вызвать функцию
pg_partition_root. В качестве аргумента<partition_table_name>необходимо указать имя партиции:SELECT * from pg_partition_root('<partition_table_name>');Эта команда возвращает одно значение в столбце
pg_partition_root— имя верхнеуровневой таблицы в иерархии партиционирования.Пример:
SELECT * FROM pg_partition_root(' sales_1_prt_4_2_prt_asia');Результат:
pg_partition_root ------------------- sales (1 row)
Операции с партициями
Чтобы добавить новую партицию в партиционированную таблицу, можно использовать команду ALTER TABLE с выражением ADD PARTITION. Начиная с ADB 7 также возможно добавить партицию с помощью команды CREATE TABLE <partition_table_name> PARTITION OF <parent_table_name> либо присоединять существующую таблицу в качестве партиции при помощи команды ALTER TABLE <parent_table_name> ATTACH PARTITION <partition_table_name>.
Следующий пример показывает создание новой партиционированной таблицы и добавление нескольких партиций после создания:
-
Создайте двухуровневую партиционированную таблицу без партиций по умолчанию:
CREATE TABLE genre_stat (id SERIAL, genre 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 (genre) SUBPARTITION TEMPLATE ( SUBPARTITION poetry VALUES ('poetry'), SUBPARTITION fantasy VALUES ('fantasy'), SUBPARTITION detective VALUES ('detective') ) (START(2013) END(2015) EVERY(1));Структура партиционирования:
SELECT * FROM pg_partition_tree('genre_stat');relid | parentrelid | isleaf | level ------------------------------------+--------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 (9 rows)
-
Добавьте новую партицию с помощью следующего запроса:
ALTER TABLE genre_stat ADD PARTITION START(2012) END(2013);Новая иерархия приведена ниже. Поскольку при создании родительской таблицы использовался шаблон, новая партиция автоматически разделяется на сабпартиции в соответствии с ним. В противном случае необходимо было бы вручную описывать каждую сабпартицию для новой партиции.
relid | parentrelid | isleaf | level -------------------------------------+---------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 (13 rows)
-
Возможно также добавление сабпартиций в существующие партиции. Для этого необходимо указать полный путь к новой сабпартиции при помощи выражений
ALTER PARTITION— по одному выражению для каждого уровня партиционирования, предшествующего новой сабпартиции:ALTER TABLE genre_stat ALTER PARTITION FOR(2012) ADD PARTITION fantastic VALUES ('fantastic');Обновленная иерархия:
relid | parentrelid | isleaf | level -------------------------------------+---------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_11 | t | 2 (14 rows)
-
Создайте двухуровневую партиционированную таблицу без партиций по умолчанию:
CREATE TABLE genre_stat (id SERIAL, genre 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); CREATE TABLE genre_stat_1_prt_1 PARTITION OF genre_stat FOR VALUES FROM (2013) TO (2014) PARTITION BY LIST (genre); CREATE TABLE genre_stat_1_prt_2 PARTITION OF genre_stat FOR VALUES FROM (2014) TO (2015) PARTITION BY LIST (genre); CREATE TABLE genre_stat_1_prt_1_2_prt_poetry PARTITION OF genre_stat_1_prt_1 FOR VALUES IN('poetry'); CREATE TABLE genre_stat_1_prt_1_2_prt_fantasy PARTITION OF genre_stat_1_prt_1 FOR VALUES IN('fantasy'); CREATE TABLE genre_stat_1_prt_1_2_prt_detective PARTITION OF genre_stat_1_prt_1 FOR VALUES IN('detective'); CREATE TABLE genre_stat_1_prt_2_2_prt_poetry PARTITION OF genre_stat_1_prt_2 FOR VALUES IN('poetry'); CREATE TABLE genre_stat_1_prt_2_2_prt_fantasy PARTITION OF genre_stat_1_prt_2 FOR VALUES IN('fantasy'); CREATE TABLE genre_stat_1_prt_2_2_prt_detective PARTITION OF genre_stat_1_prt_2 FOR VALUES IN('detective');Структура партиционирования:
SELECT * FROM pg_partition_tree('genre_stat');relid | parentrelid | isleaf | level ------------------------------------+--------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 (9 rows)
-
Добавьте новую партицию с помощью следующих запросов. Обратите внимание, что для вновь создаваемой партиции все сабпартиции прописываются вручную (как и при создании исходной партиционированной таблицы):
CREATE TABLE genre_stat_1_prt_3 PARTITION OF genre_stat FOR VALUES FROM (2012) TO (2013) PARTITION BY LIST (genre); CREATE TABLE genre_stat_1_prt_3_2_prt_poetry PARTITION OF genre_stat_1_prt_3 FOR VALUES IN('poetry'); CREATE TABLE genre_stat_1_prt_3_2_prt_fantasy PARTITION OF genre_stat_1_prt_3 FOR VALUES IN('fantasy'); CREATE TABLE genre_stat_1_prt_3_2_prt_detective PARTITION OF genre_stat_1_prt_3 FOR VALUES IN('detective');Новая иерархия приведена ниже:
relid | parentrelid | isleaf | level ------------------------------------+--------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_3 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_3_2_prt_poetry | genre_stat_1_prt_3 | t | 2 genre_stat_1_prt_3_2_prt_fantasy | genre_stat_1_prt_3 | t | 2 genre_stat_1_prt_3_2_prt_detective | genre_stat_1_prt_3 | t | 2 (13 rows)
-
Возможно также добавление сабпартиций в существующие партиции:
CREATE TABLE genre_stat_1_prt_11_2_prt_fantastic PARTITION OF genre_stat_1_prt_3 FOR VALUES IN('fantastic');Обновленная иерархия:
relid | parentrelid | isleaf | level -------------------------------------+--------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_3 | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_3_2_prt_poetry | genre_stat_1_prt_3 | t | 2 genre_stat_1_prt_3_2_prt_fantasy | genre_stat_1_prt_3 | t | 2 genre_stat_1_prt_3_2_prt_detective | genre_stat_1_prt_3 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_3 | t | 2 (14 rows)
Чтобы изменить имя партиции, можно использовать команду ALTER TABLE с выражением RENAME PARTITION. Начиная с ADB 7 также возможно переименовывать партиции напрямую — аналогично переименованию обычных таблиц с помощью команды ALTER TABLE … RENAME TO.
Следующий пример показывает переименование партиции book_1_prt_2 для родительской таблицы book, созданной выше (см. Партиционирование на основе числовых диапазонов (автоматически)).
-
Проверьте текущее имя партиции:
SELECT * FROM pg_partition_tree('book');Результат приведен ниже. Одна из партиций имеет имя
book_1_prt_2:relid | parentrelid | isleaf | level ------------------+-------------+--------+------- book | | f | 0 book_1_prt_other | book | t | 1 book_1_prt_2 | book | t | 1 book_1_prt_3 | book | t | 1 book_1_prt_4 | book | t | 1 book_1_prt_5 | book | t | 1 book_1_prt_6 | book | t | 1 book_1_prt_7 | book | t | 1 book_1_prt_8 | book | t | 1 book_1_prt_9 | book | t | 1 book_1_prt_10 | book | t | 1 book_1_prt_11 | book | t | 1 (12 rows)
-
Переименуйте партицию, как указано ниже:
-
Проверьте имя партиции повторно (используя запрос с шага 1). Имя партиции
relidобновлено (book_1_prt_year2013вместоbook_1_prt_2):relid | parentrelid | isleaf | level ---------------------+-------------+--------+------- book | | f | 0 book_1_prt_other | book | t | 1 book_1_prt_year2013 | book | t | 1 book_1_prt_3 | book | t | 1 book_1_prt_4 | book | t | 1 book_1_prt_5 | book | t | 1 book_1_prt_6 | book | t | 1 book_1_prt_7 | book | t | 1 book_1_prt_8 | book | t | 1 book_1_prt_9 | book | t | 1 book_1_prt_10 | book | t | 1 book_1_prt_11 | book | t | 1 (12 rows)
Обратите внимание, что изменения в имени родительской таблицы также распространяются на имена дочерних партиций. Например, следующая команда переименовывает таблицу book в magazine:
ALTER TABLE book RENAME TO magazine;
Изменения будут применены ко всем дочерним партициям. Чтобы убедиться в этом, можно выполнить запрос:
SELECT * FROM pg_partition_tree('magazine');
Результат:
relid | parentrelid | isleaf | level -------------------------+-------------+--------+------- magazine | | f | 0 magazine_1_prt_other | magazine | t | 1 magazine_1_prt_year2013 | magazine | t | 1 magazine_1_prt_3 | magazine | t | 1 magazine_1_prt_4 | magazine | t | 1 magazine_1_prt_5 | magazine | t | 1 magazine_1_prt_6 | magazine | t | 1 magazine_1_prt_7 | magazine | t | 1 magazine_1_prt_8 | magazine | t | 1 magazine_1_prt_9 | magazine | t | 1 magazine_1_prt_10 | magazine | t | 1 magazine_1_prt_11 | magazine | t | 1 (12 rows)
Чтобы добавить партицию по умолчанию в существующую партиционированную таблицу, можно использовать команду ALTER TABLE с выражением ADD DEFAULT PARTITION. Начиная с ADB 7 также возможно отмечать новую партицию как дефолтную при ее создании (с помощью команды CREATE TABLE <partition_table_name> PARTITION OF <parent_table_name> DEFAULT) либо присоединять существующую таблицу в качестве партиции по умолчанию (при помощи команды ALTER TABLE <parent_table_name> ATTACH PARTITION <partition_table_name> DEFAULT).
Следующий пример показывает добавление партиции по умолчанию на первом уровне иерархии в таблицу genre_stat, созданную выше (см. Добавление новой партиции):
ALTER TABLE genre_stat ADD DEFAULT PARTITION other;
Обновленная иерархия приведена ниже. Все сабпартиции создаются автоматически в соответствии с шаблоном, использованным при создании таблицы.
SELECT * FROM pg_partition_tree('genre_stat');
Результат:
relid | parentrelid | isleaf | level ----------------------------------------+------------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_other | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_other_2_prt_poetry | genre_stat_1_prt_other | t | 2 genre_stat_1_prt_other_2_prt_fantasy | genre_stat_1_prt_other | t | 2 genre_stat_1_prt_other_2_prt_detective | genre_stat_1_prt_other | t | 2 (18 rows)
CREATE TABLE genre_stat_1_prt_other PARTITION OF genre_stat DEFAULT;
В качестве альтернативы можно использовать следующие команды:
CREATE TABLE genre_stat_1_prt_other (LIKE genre_stat);
ALTER TABLE genre_stat ATTACH PARTITION genre_stat_1_prt_other DEFAULT;
Обновленная иерархия приведена ниже. Обратите внимание, что в отличие от примера с классическим синтаксисом сабпартиции для новой партиции автоматически не создаются — при необходимости их следует создать вручную.
SELECT * FROM pg_partition_tree('genre_stat');
Результат:
relid | parentrelid | isleaf | level -------------------------------------+---------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_other | genre_stat | t | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_11 | t | 2 (15 rows)
Возможно также добавление партиций по умолчанию на других уровнях иерархии. Следующий запрос создает партицию по умолчанию на втором уровне иерархии:
Обратите внимание, что необходимо указать полный путь к новой партиции при помощи выражений ALTER PARTITION — по одному выражению для каждого уровня партиционирования, предшествующего партиции по умолчанию.
ALTER TABLE genre_stat ALTER PARTITION FOR (2013) ADD DEFAULT PARTITION other;
Обновленная иерархия:
relid | parentrelid | isleaf | level ----------------------------------------+------------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_other | genre_stat | f | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_other | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_other_2_prt_poetry | genre_stat_1_prt_other | t | 2 genre_stat_1_prt_other_2_prt_fantasy | genre_stat_1_prt_other | t | 2 genre_stat_1_prt_other_2_prt_detective | genre_stat_1_prt_other | t | 2 (19 rows)
CREATE TABLE genre_stat_1_prt_1_2_prt_other PARTITION OF genre_stat_1_prt_1 DEFAULT;
Обновленная иерархия:
relid | parentrelid | isleaf | level -------------------------------------+---------------------+--------+------- genre_stat | | f | 0 genre_stat_1_prt_1 | genre_stat | f | 1 genre_stat_1_prt_2 | genre_stat | f | 1 genre_stat_1_prt_11 | genre_stat | f | 1 genre_stat_1_prt_other | genre_stat | t | 1 genre_stat_1_prt_1_2_prt_poetry | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_fantasy | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_detective | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_1_2_prt_other | genre_stat_1_prt_1 | t | 2 genre_stat_1_prt_2_2_prt_poetry | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_fantasy | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_2_2_prt_detective | genre_stat_1_prt_2 | t | 2 genre_stat_1_prt_11_2_prt_poetry | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantasy | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_detective | genre_stat_1_prt_11 | t | 2 genre_stat_1_prt_11_2_prt_fantastic | genre_stat_1_prt_11 | t | 2 (16 rows)
Для разделения партиции на две необходимо использовать команду ALTER TABLE с выражением SPLIT PARTITION.
Следующий пример показывает разделение партиций в родительской таблице book_order, созданной выше (см. Партиционирование на основе диапазонов дат (автоматически)). Синтаксис команды актуален и для ADB 6, и для ADB 7:
-
Разделите партицию, хранящую данные за январь 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 года.SELECT * FROM pg_partition_tree('book_order');Результат:
relid | parentrelid | isleaf | level ----------------------------+-------------+--------+------- book_order | | f | 0 book_order_1_prt_other | book_order | t | 1 book_order_1_prt_3 | book_order | t | 1 book_order_1_prt_4 | book_order | t | 1 book_order_1_prt_5 | book_order | t | 1 book_order_1_prt_6 | book_order | t | 1 book_order_1_prt_7 | book_order | t | 1 book_order_1_prt_8 | book_order | t | 1 book_order_1_prt_9 | book_order | t | 1 book_order_1_prt_10 | book_order | t | 1 book_order_1_prt_11 | book_order | t | 1 book_order_1_prt_12 | book_order | t | 1 book_order_1_prt_13 | book_order | t | 1 book_order_1_prt_jan1to15 | book_order | t | 1 book_order_1_prt_jan16to31 | book_order | t | 1 (15 rows)
|
ПРИМЕЧАНИЕ
|
Операция обмена (exchange) позволяет заменять партицию на таблицу с аналогичной структурой. Эту таблицу принято называть staging-таблицей. После применения операции staging-таблица содержит данные партиции, а партиция — данные staging-таблицы. Этот функционал особенно полезен для загрузки данных, поскольку вставка данных непосредственно в сами партиции не рекомендуется в силу своей неэффективности. Обмен партиции также позволяет изменять тип хранения данных в существующих партициях.
Чтобы выполнить обмен партиции, можно использовать команду ALTER TABLE с выражением EXCHANGE PARTITION. В качестве альтернативы начиная с ADB 7 возможно отсоединить существующую партицию (с помощью команды DETACH) и затем присоединить вместо нее новую партицию с необходимыми данными.
Следующий пример показывает обмен партиции для таблицы book_order_manual, созданной выше (см. Партиционирование на основе диапазонов дат (вручную)):
-
Просмотрите родительскую таблицу вместе со всеми партициями, используя команду
psql\dt+:\dt+ book_order_manual*
Результат:
List of relations Schema | Name | Type | Owner | Storage | Size | Description --------+-------------------------------+-------------------+---------+---------+---------+------------- public | book_order_manual | partitioned table | gpadmin | ao_row | 0 bytes | public | book_order_manual_1_prt_apr22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_aug22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_dec22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_feb22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jan22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jul22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jun22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_mar22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_may22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_nov22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_oct22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_other | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_sep22 | table | gpadmin | ao_row | 288 kB | (14 rows)
-
Убедитесь, что партиция
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)
-
Создайте новую таблицу с той же структурой, что у партиционированной таблицы
book_order_manual. Выберите другой тип хранения данных (heap):CREATE TABLE dec22 (LIKE book_order_manual) WITH (appendoptimized=false); -
Добавьте данные в новую таблицу:
INSERT INTO dec22(id, book_id, client_id, book_count, order_date) VALUES(1, 1, 1, 1, '2022-12-01'); -
Замените партицию
book_order_manual_1_prt_dec22новой таблицей.
ALTER TABLE book_order_manual EXCHANGE PARTITION FOR (DATE '2022-12-01')
WITH TABLE dec22;
ALTER TABLE book_order_manual DETACH PARTITION book_order_manual_1_prt_dec22;
ALTER TABLE book_order_manual ATTACH PARTITION dec22 FOR VALUES FROM ('2022-12-01') TO ('2023-01-01');
ALTER TABLE book_order_manual_1_prt_dec22 RENAME TO detached;
ALTER TABLE dec22 RENAME TO book_order_manual_1_prt_dec22;
-
Проверьте, что партиция содержит данные (используя запрос с шага 2):
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)
-
Убедитесь, что тип хранения данных в партиции также изменился (используя
psql-команду с шага 1):\dt+ book_order_manual*
Результат:
List of relations Schema | Name | Type | Owner | Storage | Size | Description --------+-------------------------------+-------------------+---------+---------+---------+------------- public | book_order_manual | partitioned table | gpadmin | ao_row | 0 bytes | public | book_order_manual_1_prt_apr22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_aug22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_dec22 | table | gpadmin | heap | 32 kB | public | book_order_manual_1_prt_feb22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jan22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jul22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_jun22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_mar22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_may22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_nov22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_oct22 | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_other | table | gpadmin | ao_row | 288 kB | public | book_order_manual_1_prt_sep22 | table | gpadmin | ao_row | 288 kB | (14 rows)
|
ПРИМЕЧАНИЕ
|
Чтобы удалить все данные из партиции без удаления самой партиции, можно использовать команду ALTER TABLE с выражением TRUNCATE PARTITION. Начиная с ADB 7 также возможно удалять данные из партиции напрямую — аналогично удалению данных из обычных таблиц с помощью команды TRUNCATE.
Следующий пример показывает удаление данных из партиции sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction, которые были добавлены выше:
-
Проверьте наличие данных в партиции:
SELECT * FROM sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;Результат:
id | date | fiction | region ----+------------+---------+-------- 3 | 2022-01-03 | f | asia 2 | 2022-01-02 | f | asia 1 | 2022-01-01 | f | asia (3 rows)
-
Удалите данные из партиции.
Обратите внимание, что для таблиц с многоуровневым партиционированием необходимо указывать полный путь к дочерней партиции, используя выражения ALTER PARTITION для описания каждого уровня партиционирования, предшествующего искомой партиции:
ALTER TABLE sales_manual
ALTER PARTITION jan2022
ALTER PARTITION asia
TRUNCATE PARTITION fiction;
Удаление данных только из выбранной партиции:
TRUNCATE ONLY sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;
Удаление данных из партиционированной таблицы целиком:
TRUNCATE sales_manual;
-
Убедитесь, что партиция более не содержит данных (используя запрос с шага 1):
SELECT * FROM sales_manual_1_prt_jan2022_2_prt_asia_3_prt_fiction;Результат:
id | date | fiction | region ----+------+---------+-------- (0 rows)
|
ПРИМЕЧАНИЕ
При удалении данных из партиции, имеющей сабпартиции, данные из последних также автоматически удаляются.
|
Чтобы удалить партицию, можно использовать команду ALTER TABLE с выражением DROP PARTITION. Начиная с ADB 7 также возможно удалять партиции напрямую — аналогично удалению обычных таблиц с помощью команды DROP TABLE.
Следующий пример показывает удаление партиции year2013 из таблицы book_manual, созданной выше (см. Партиционирование на основе числовых диапазонов (вручную)):
-
Проверьте существование партиции:
SELECT * FROM pg_partition_tree('book_manual');Результат:
relid | parentrelid | isleaf | level ----------------------------+-------------+--------+------- book_manual | | f | 0 book_manual_1_prt_other | book_manual | t | 1 book_manual_1_prt_year2013 | book_manual | t | 1 book_manual_1_prt_year2014 | book_manual | t | 1 book_manual_1_prt_year2015 | book_manual | t | 1 book_manual_1_prt_year2016 | book_manual | t | 1 book_manual_1_prt_year2017 | book_manual | t | 1 book_manual_1_prt_year2018 | book_manual | t | 1 book_manual_1_prt_year2019 | book_manual | t | 1 book_manual_1_prt_year2020 | book_manual | t | 1 book_manual_1_prt_year2021 | book_manual | t | 1 book_manual_1_prt_year2022 | book_manual | t | 1 (12 rows)
-
Удалите партицию:
-
Убедитесь, что партиция более не существует (используя запрос с шага 1):
relid | parentrelid | isleaf | level ----------------------------+-------------+--------+------- book_manual | | f | 0 book_manual_1_prt_other | book_manual | t | 1 book_manual_1_prt_year2014 | book_manual | t | 1 book_manual_1_prt_year2015 | book_manual | t | 1 book_manual_1_prt_year2016 | book_manual | t | 1 book_manual_1_prt_year2017 | book_manual | t | 1 book_manual_1_prt_year2018 | book_manual | t | 1 book_manual_1_prt_year2019 | book_manual | t | 1 book_manual_1_prt_year2020 | book_manual | t | 1 book_manual_1_prt_year2021 | book_manual | t | 1 book_manual_1_prt_year2022 | book_manual | t | 1 (11 rows)
|
ПРИМЕЧАНИЕ
При удалении партиции, имеющей сабпартиции, последние автоматически удаляются (вместе со всеми данными). |
Операция отсоединения (detach), доступная начиная с ADB 7, позволяет исключить выбранную партицию из иерархии партиционирования, однако не удаляет ее. Это может быть полезно для выполнения каких-либо работ над данными перед их окончательным удалением. Чтобы отсоединить партицию, используйте команду ALTER TABLE … DETACH PARTITION.
Следующий пример показывает отсоединение партиции book_manual_1_prt_year2021 из таблицы book_manual, созданной выше (см. Партиционирование на основе числовых диапазонов (вручную)):
-
Проверьте существование партиции:
SELECT * FROM pg_partition_tree('book_manual');Результат:
relid | parentrelid | isleaf | level ----------------------------+-------------+--------+------- book_manual | | f | 0 book_manual_1_prt_other | book_manual | t | 1 book_manual_1_prt_year2014 | book_manual | t | 1 book_manual_1_prt_year2015 | book_manual | t | 1 book_manual_1_prt_year2016 | book_manual | t | 1 book_manual_1_prt_year2017 | book_manual | t | 1 book_manual_1_prt_year2018 | book_manual | t | 1 book_manual_1_prt_year2019 | book_manual | t | 1 book_manual_1_prt_year2020 | book_manual | t | 1 book_manual_1_prt_year2021 | book_manual | t | 1 book_manual_1_prt_year2022 | book_manual | t | 1 (11 rows)
-
Отсоедините партицию:
ALTER TABLE book_manual DETACH PARTITION book_manual_1_prt_year2021; -
Убедитесь, что партиция исключена из иерархии партиционирования (используя запрос с шага 1):
relid | parentrelid | isleaf | level ----------------------------+-------------+--------+------- book_manual | | f | 0 book_manual_1_prt_other | book_manual | t | 1 book_manual_1_prt_year2014 | book_manual | t | 1 book_manual_1_prt_year2015 | book_manual | t | 1 book_manual_1_prt_year2016 | book_manual | t | 1 book_manual_1_prt_year2017 | book_manual | t | 1 book_manual_1_prt_year2018 | book_manual | t | 1 book_manual_1_prt_year2019 | book_manual | t | 1 book_manual_1_prt_year2020 | book_manual | t | 1 book_manual_1_prt_year2022 | book_manual | t | 1 (10 rows)
-
Проверьте, что партиция не удалена:
\dt+ book_manual*
Результат:
List of relations Schema | Name | Type | Owner | Storage | Size | Description --------+----------------------------+-------------------+---------+---------+---------+------------- public | book_manual | partitioned table | gpadmin | ao_row | 0 bytes | public | book_manual_1_prt_other | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2014 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2015 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2016 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2017 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2018 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2019 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2020 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2021 | table | gpadmin | ao_row | 192 kB | public | book_manual_1_prt_year2022 | table | gpadmin | ao_row | 192 kB | (11 rows)
|
ПРИМЕЧАНИЕ
При отсоединении партиции, имеющей сабпартиции, последние автоматически отсоединяются. |