#sql #database #oracle
Вопрос:
Я только что обнаружил, что Oracle позволяет вам делать следующее:
SELECT foo.a, (SELECT c
FROM bar
WHERE foo.a = bar.a)
from foo
До тех пор, пока только одна строка в строке соответствует любой строке в foo.
План объяснения, который я получаю от разработчика PL/SQL, таков:
SELECT STATEMENT, GOAL = ALL_ROWS
TABLE ACCESS FULL BAR
TABLE ACCESS FULL FOO
На самом деле это не определяет, как соединяются таблицы. Коллега утверждал, что это более эффективно, чем обычное объединение. Это правда? Какова стратегия объединения для такого оператора select и почему она не отображается в плане объяснения?
Спасибо.
Ответ №1:
План, который у вас там есть, вообще не содержит много информации.
Используйте SQL*Plus и используйте dbms_xplan, чтобы получить более подробный план. Найдите скрипт под названием utlxpls.sql.
Это дает немного больше информации:-
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1837 | 23881 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| BAR | 18 | 468 | 2 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| FOO | 1837 | 23881 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("BAR"."A"=:B1)
Note
-----
- dynamic sampling used for this statement
18 rows selected.
Я не создавал никаких индексов или внешних ключей или не собирал статистику по таблицам, что изменило бы план и выбранный механизм объединения. Oracle на самом деле выполняет объединение типов ВЛОЖЕННЫХ ЦИКЛОВ здесь. Шаг 1, ваш встроенный суб-выбор, выполняется для каждой строки, возвращаемой из FOO.
Этот способ выполнения ВЫБОРА не является более быстрым. Это может быть то же самое или медленнее. В общем, попробуйте объединить все в главном предложении WHERE, если оно не станет ужасно нечитабельным.
Комментарии:
1. Спасибо, это то, что я подозревал. Но почему вложенный цикл не отображается в плане? Я согласен, что это обратный способ делать вещи, и я удивлен, что Oracle даже позволяет вам писать такие вещи. Но я должен был проверить заявления об эффективности.
2. Да, эти запросы ужасны. Вместо этого сделайте обычное соединение.
3. Эти столы жалко маленькие. Возможно, оптимизатор Oracle просто не хочет слишком усердно работать, чтобы составить сложный план?
Ответ №2:
Если вы создадите обычный индекс на панели(a), CBO сможет его использовать, но я почти уверен, что он не сможет выполнять хэш-соединения. Такого рода запросы имеют смысл только в том случае, если вы используете агрегатную функцию и у вас есть несколько однострочных подзапросов в верхнем списке выбора. Тем не менее, вы всегда можете переписать запрос как:
SELECT foo.a, bar1.c, pub1.d
FROM foo
JOIN (SELECT a, MIN(c) as c
FROM bar
GROUP BY a) bar1
ON foo.a = bar1.a
JOIN (SELECT a, MAX(d) as d
FROM pub
GROUP BY a) pub1
ON foo.a = pub1.a
Это позволило бы CBO использовать больше опций, в то же время это позволило бы вам легко извлекать несколько столбцов из дочерних таблиц без необходимости многократного сканирования одних и тех же таблиц.