Когда Postgresql сокращает разделы с помощью столбцов JOIN?

У меня есть две таблицы в базе данных Postgres 11:

client table
--------
client_id    integer
client_name  character_varying

file table
--------
file_id      integer
client_id    integer
file_name    character_varying

Клиентская таблица не секционирована, файловая таблица секционирована по client_id (секционирование по списку). Когда новый клиент вставляется в таблицу клиентов, триггер создает новый раздел для таблицы файлов. Таблица файлов имеет ограничение внешнего ключа, ссылающееся на клиентскую таблицу по client_id.

Когда я выполняю этот SQL (где c.client_id = 1), все выглядит нормально:

explain  
select *
from client c
join file f using (client_id)
where c.client_id = 1;

Используется сокращение раздела, проверяется только раздел file_p1:

Nested Loop  (cost=0.00..3685.05 rows=100001 width=82)
  ->  Seq Scan on client c  (cost=0.00..1.02 rows=1 width=29)
        Filter: (client_id = 1)
  ->  Append  (cost=0.00..2684.02 rows=100001 width=57)
        ->  Seq Scan on file_p1 f  (cost=0.00..2184.01 rows=100001 width=57)
              Filter: (client_id = 1)

Но когда я использую предложение where, например «where c.client_name = 'test'», база данных сканирует все разделы и не распознает, что client_name «test» равно client_id 1:

explain  
select *
from client c
join file f using (client_id)
where c.client_name = 'test';

План выполнения:

Hash Join  (cost=1.04..6507.57 rows=100001 width=82)
  Hash Cond: (f.client_id = c.client_id)
  ->  Append  (cost=0.00..4869.02 rows=200002 width=57)
        ->  Seq Scan on file_p1 f  (cost=0.00..1934.01 rows=100001 width=57)
        ->  Seq Scan on file_p4 f_1  (cost=0.00..1934.00 rows=100000 width=57)
        ->  Seq Scan on file_pdefault f_2  (cost=0.00..1.00 rows=1 width=556)
  ->  Hash  (cost=1.02..1.02 rows=1 width=29)
        ->  Seq Scan on client c  (cost=0.00..1.02 rows=1 width=29)
              Filter: ((name)::text = 'test'::text)

Таким образом, для этого SQL сканируются все разделы в файловой таблице.

Так должен ли каждый выбор использовать столбец, по которому разбиты таблицы? Может ли база данных отклониться от критериев сокращения разделов?


Изменить: чтобы добавить информацию:

Раньше я большую часть времени работал с базами данных Oracle.

План выполнения там был бы что-то вроде

  1. Выполните полное сканирование таблицы клиента с именем клиента, чтобы узнать client_id.
  2. Выполните доступ «СПИСОК РАЗДЕЛОВ» к таблице файлов, где разработчик SQL указывает PARTITION_START = KEY и PARTITION_STOP = KEY, чтобы указать, что точный раздел не известен при расчете плана выполнения, но доступ будет осуществляться только к списку разделов, которые рассчитываются на основе client_id, найденного в таблице client.

Это то, чего я ожидал и от Postgresql.


person BrooklynFFM    schedule 29.09.2019    source источник
comment
План запроса создается до выполнения запроса, поэтому он не может быть основан на результатах запроса. Другими словами, планировщик не может знать, что client_name = 'test' означает client_id = 1, до выполнения запроса.   -  person klin    schedule 29.09.2019
comment
Спасибо за комментарий, я добавил дополнительную информацию, которую я ожидал получить из базы данных Oracle.   -  person BrooklynFFM    schedule 29.09.2019


Ответы (1)


В документации указано, что динамическое сокращение раздела возможный

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

Если я правильно понимаю, это применимо к подготовленным операторам или запросам с подзапросами, которые предоставляют значение ключа раздела в качестве параметра. Используйте explain analyse, чтобы увидеть динамическую обрезку (мой пример данных содержит миллион строк в трех разделах):

explain analyze
select *
from file
where client_id = (
    select client_id
    from client
    where client_name = 'test');

Append  (cost=25.88..22931.88 rows=1000000 width=14) (actual time=0.091..96.139 rows=333333 loops=1)
  InitPlan 1 (returns $0)
    ->  Seq Scan on client  (cost=0.00..25.88 rows=6 width=4) (actual time=0.040..0.042 rows=1 loops=1)
          Filter: (client_name = 'test'::text)
          Rows Removed by Filter: 2
  ->  Seq Scan on file_p1  (cost=0.00..5968.66 rows=333333 width=14) (actual time=0.039..70.026 rows=333333 loops=1)
        Filter: (client_id = $0)
  ->  Seq Scan on file_p2  (cost=0.00..5968.68 rows=333334 width=14) (never executed)
        Filter: (client_id = $0)
  ->  Seq Scan on file_p3  (cost=0.00..5968.66 rows=333333 width=14) (never executed)
        Filter: (client_id = $0)
Planning Time: 0.423 ms
Execution Time: 109.189 ms

Обратите внимание, что сканирование разделов p2 и p3 было never executed.

Отвечая на ваш точный вопрос, сокращение разделов в запросах с объединениями, описанное в вопросе, не реализовано в Postgres (пока?)

person klin    schedule 29.09.2019
comment
Большое спасибо! Это полностью отвечает на мой вопрос. Комбинация использования подзапроса и выполнения анализа объяснения была ключевой. - person BrooklynFFM; 03.10.2019