Почему Oracle выполняет как TABLE SCAN, так и INDEX UNIQUE SCAN для одной и той же таблицы в одном запросе?

Я не ищу оптимизацию следующего, просто объяснение. У меня есть этот запрос:

SELECT COUNT(LARGE_A.id_a), SUM(LARGE_A.b_integer)
FROM LARGE_A
INNER JOIN MEDIUM_A ON LARGE_A.id_a = MEDIUM_A.id
INNER JOIN MEDIUM_B ON LARGE_A.id_b = MEDIUM_B.id
WHERE
    MEDIUM_A.a_varchar2 LIKE 'Example%' AND
    EXTRACT(YEAR FROM MEDIUM_B.a_datetime) = 2000 AND
    LARGE_A.a_integer BETWEEN 0 AND 1000;

Типы атрибутов записываются в их именах. Идентификаторы являются целыми числами. В таблице LARGE_A 1 000 000 строк, в средних по 100 000. Идентификаторы являются первичными ключами, однако таблица LARGE_A имеет составной ключ (id_a, id_b).

Затем у меня есть следующий план выполнения, взятый из представления V$SQL, потому что Autotrace лжет об этом.

SELECT STATEMENT                        
|SORT AGGREGATE                         
||HASH JOIN                             
|||NESTED LOOPS                         
||||NESTED LOOPS                        
|||||STATISTICS COLLECTOR               
||||||NESTED LOOPS                      
|||||||TABLE ACCESS FULL                LARGE_A
|||||||TABLE ACCESS BY INDEX ROWID      MEDIUM_A
||||||||INDEX UNIQUE SCAN               PK(MEDIUM_A)
|||||INDEX UNIQUE SCAN                  PK(MEDIUM_B)
||||TABLE ACCESS BY INDEX ROWID         MEDIUM_B
|||TABLE ACCESS FULL                    MEDIUM_B

PK означает некластеризованный индекс по первичному ключу (целому числу) таблицы, указанной в скобках. Я не использую IOTs здесь.

Зачем оптимизатору сначала запрашивать записи из индекса одну за другой PK(MEDIUM_B), затем извлекать остальные строки, используя ROWID, а затем снова запускать ПОЛНОЕ сканирование таблицы?

Было бы полезно обновить ваш вопрос с помощью оператора SQL, который вы используете.

NickW 09.04.2023 16:40

@NickW Ну вот.

sanitizedUser 09.04.2023 16:49

Наверное, неактуально, но это: EXTRACT(YEAR FROM MEDIUM_B.a_datetime) = 0 бессмысленно. Год не может быть равен 0 (в Oracle). Если вы попробуете это, например. to_date('01.01.0000', 'dd.mm.yyyy') вы получите ORA-01841: (full) year must be between -4713 and +9999, and not be 0. Итак, если у вас действительно есть это состояние, просто удалите его, так как оно никогда не может быть правдой.

Littlefoot 09.04.2023 18:27

@Littlefoot Извини, я исправлю. В реальном запросе, конечно, есть год от современности.

sanitizedUser 09.04.2023 18:30
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
4
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

То, что вы видите, является полным планом, а не фактическим планом (в v$sql разница между full_plan_hash_value и plan_hash_value). Начиная с версии 12c, у Oracle есть адаптивные планы, что дает ему возможность переходить с одного плана на другой в процессе выполнения, если во время выполнения он видит, что его догадки о количестве элементов далеки от ожидаемых. Полный план дает оба варианта — либо хеш-соединение + полное сканирование таблицы B, либо вложенные циклы + индексный доступ к таблице B. Все зависит от того, сколько строк он получает из таблицы A, чтобы выбрать лучший план.

Вы можете увидеть это лучше всего так:

EXPLAIN PLAN FOR ....
/
SELECT * FROM TABLE(dbms_xplan.display(format=>'ADAPTIVE'))
/

Вы увидите отметки - рядом с отключенными строками плана, которые не будут использоваться по умолчанию... но они все еще являются частью плана, поэтому, если Oracle решит переключиться в середине выполнения, он может включить эти строки и отключить исходные значения по умолчанию. Чтобы увидеть, что на самом деле используется вашим кодом во время выполнения, вы должны наблюдать за этим, получив v$session.sql_id / sql_child_number, а затем выполните:

SELECT * FROM TABLE(dbms_xplan.display_cursor([sqlid],[childnumber]))

/

Значит, Autotrace отображает правильный план? Тот, который фактически выполняется как последний (изменен на средний полет)?

sanitizedUser 09.04.2023 17:52

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

Paul W 09.04.2023 18:34

Другие вопросы по теме