объединить метод выбора в списке столбцов - PullRequest
2 голосов
/ 19 марта 2019

это вопрос 11g, но я думаю, что это не зависит от версии.

У меня есть этот простой выбор:

create table tab_a (id number);
create table tab_b (id number);
create table tab_c (id number);

select 
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;

Что дает мне такой план:

SELECT LPAD(' ',depth)||OPERATION||'_'||OPTIONS||' '||OBJECT_NAME plan
FROM v$sql_plan
WHERE plan_hash_value = 2530031923
order by id;
SELECT STATEMENT_ 
 TABLE ACCESS_FULL TAB_C
 HASH JOIN_ 
  TABLE ACCESS_FULL TAB_A
  TABLE ACCESS_FULL TAB_B

Мой вопрос сейчас, пожалуйста: как TAB_C присоединяется к результату хеш-соединения TAB_A и TAB_B? Доступ к нему во вложенном цикле один раз для каждого результата хеш-соединения? Это хеш-соединение с TAB_C в качестве таблицы управления? Сортировка-слияние? Что-то совершенно другое? Могут ли быть разные методы соединения для TAB_C за этим планом, или это всегда один и тот же?

Большое спасибо!

1 Ответ

3 голосов
/ 19 марта 2019

Прежде всего вы должны использовать немного более новую технику для проверки плана выполнения, например, DBMS_XPLAN.DISPLAY`

EXPLAIN PLAN  SET STATEMENT_ID = 'sqlx' into   plan_table  FOR
select 
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;


SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'sqlx','ALL'));    

    ----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |   100 |  2600 |     7  (15)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL | TAB_C |     1 |    13 |     3   (0)| 00:00:01 |
|*  2 |  HASH JOIN         |       |   100 |  2600 |     7  (15)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| TAB_A |   100 |  1300 |     3   (0)| 00:00:01 |
|   4 |   TABLE ACCESS FULL| TAB_B |   100 |  1300 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("C"."ID"=:B1)
   2 - access("A"."ID"="B"."ID")

Это дает вам информацию о том, как осуществляется доступ к TAB_C. Вы видите в Информация о предикате для строки 1 filter("C"."ID"=:B1).

Другими словами, вы будете полностью сканировать таблицу для каждого ID, объединенного между таблицами A и B. Что, конечно, нежелательно.

Если вы не доверяете этому простому, запустите запрос и соберите статистику плана

select /*+ gather_plan_statistics */
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;  

---
select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST')); 

SQL_ID  4m4a1cp4gyjkv, child number 0
-------------------------------------
select /*+ gather_plan_statistics */     a.id,b.id,(select c.id from 
tab_c c where c.id = a.id)  from      tab_a a join tab_b b on a.id = 
b.id

Plan hash value: 2606630813

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |      1 |        |    100 |00:00:00.01 |      15 |       |       |          |
|*  1 |  TABLE ACCESS FULL | TAB_C |    100 |      1 |    100 |00:00:00.01 |     700 |       |       |          |
|*  2 |  HASH JOIN         |       |      1 |    100 |    100 |00:00:00.01 |      15 |  1517K|  1517K| 1256K (0)|
|   3 |   TABLE ACCESS FULL| TAB_A |      1 |    100 |    100 |00:00:00.01 |       7 |       |       |          |
|   4 |   TABLE ACCESS FULL| TAB_B |      1 |    100 |    100 |00:00:00.01 |       8 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("C"."ID"=:B1)
   2 - access("A"."ID"="B"."ID")

В строке 1 вы видите начинается = 100, что означает 100 раз, когда FULL SCAN был инициирован.

Внимание - план выполнения может меняться в зависимости от статистики таблицы, настроек оптимизатора или версии Oracle (например, Oracle может переписать подзапрос и использовать объединение).

Это только пример с фиктивными таблицами на 11.2. Но у вас должно сложиться впечатление, как наблюдать за поведением Oracle и решить, нужен ли вам дополнительный индекс или нет.

...