Hash Join с ограничением раздела из третьей таблицы - PullRequest
0 голосов
/ 07 декабря 2018

моя текущая проблема в 11g, но меня также интересует, как это можно решить умнее в более поздних версиях.

Я хочу объединить две таблицы.Таблица A содержит 10 миллионов строк, таблица B огромна и содержит миллиард записей на тысяче разделов.Один раздел содержит около 10 миллионов записей.Я не присоединяюсь к ключу раздела.Для большинства строк таблицы A будет найдена одна или несколько строк в таблице B.Пример:

select * from table_a a
inner join table_b b on a.ref = b.ref

Приведенное выше вернет около 50 миллионов строк, тогда как результаты будут получены примерно из 30 разделов таблицы b.Я предполагаю, что хеш-соединение - это правильное соединение, хеш-таблица a и таблица FTSing / index-scan b.

Итак, 970 разделов были отсканированы без причины.И у меня есть третий запрос, который может сказать оракулу, какие 30 разделов нужно проверить на соединение.Пример третьего запроса:

select partition_id from table_c

Этот запрос дает ровно 30 разделов для запроса выше.

На мой вопрос:

В PL / SQL это можно решить

  1. выберите 30 partition_ids в переменную (будь это просто select listagg(partition_id,',') ... into v_partitions from table_c
  2. Выполните мой запрос следующим образом:

    execute immediate 'select * from table_a a 
    inner join table_b b on a.ref = b.ref 
    where b.partition_id in ('||v_partitions||')' into ...
    

Допустим, это завершится за 10 минут.

Теперь, как я могу сделать это за такое же время с чистым SQL?

Простонаписание

select * from table_a a
inner join table_b b on a.ref = b.ref 
where b.partition_id in (select partition_id from table_c)

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

План, который, я думаю, мне нужен, -

hash join
    table a
    nested loop
       table c
       partition pruning here
           table b

Но это не возвращается через 10 минут.

Итак, как это сделать в SQL и к какому плану выполнения стремиться? Один вариант, который я еще не пробовал, может быть решением:

nested loop
   table c
   hash join
       table a
       partition pruning here (pushed predicate from the join to c)
            table b

Другое чувство, которое у меня возникает, заключается в том, что решение может состоять в том, чтобы соединить таблицу a с таблицей c (хотя не уверен, что), а затем соединить этот результат с табуляцией.le b.

Я не прошу вас напечатать все для меня.Просто общее представление о том, как это сделать (получить ограничение раздела из запроса) в SQL - к какому плану мне стремиться?

большое спасибо!Питер

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

спасибо всем за ваши обсуждения со мной по этому вопросу.В моем случае это было решено (не мной) путем добавления пути соединения между table_c и table_a и перегрузки условий соединения, как показано ниже.В моем случае это было возможно, добавив столбец partition_id в table_a:

select * from
  table_c c
  JOIN table_a a ON (a.partition_id = c.partition_id)
  JOIN table_b b ON (b.partition_id = c.partition_id and b.partition_id = a.partition_id and b.ref = a.ref)

И это план, который вы хотите:

leading(c,b,a) use_nl(c,b) swap_join_inputs(a) use_hash(a)

Итак, вы получите:

hash join
    table a
    nested loop
       table c
       partition list iterator
           table b
0 голосов
/ 07 декабря 2018

Я не эксперт в этом, но я думаю, что Oracle обычно сначала выполняет объединения, а затем применяет условия where.Таким образом, вы можете получить нужный план, переместив сокращение раздела в состояние соединения:

select * from table_a a
inner join table_b b on a.ref = b.ref 
  and b.partition_id in (select partition_id from table_c);

Я также видел, как люди пытались делать такие вещи с помощью встроенного представления:

select * from table_a a
inner join (select * from table_b
            where partition_id in (select partition_id from table_c)) b
on a.ref = b.ref;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...