Почему используется CARTESIAN JOIN?
Как вы указали, в таблице не более одной записи CLASSES
с данным item_id
.Oracle понимает это (см. Столбец Rows
в строке 9 плана выполнения) и решает сделать декартово объединение этой одной строки с ожидаемыми 9 строками таблицы ASSIGNMENTS
.
Это прекрасно работает , если оценка мощности в порядке, но может вызвать большие проблемы, если она неточна.Я полагаю, что item_id
не поддерживается первичным ключом или уникальным индексом - см. INDEX RANGE SCAN
в строке 9;Я бы ожидал INDEX UNIQUE SCAN
в случае уникального индекса.Так что это потенциальная причина проблем.
Использование WITH
предложений
Как уже упоминалось, предложения WITH
в вашем запросе не действуют, нотакже не причиняет вреда.Как вы видите в плане выполнения, они объединены в основном запросе.Oracle переписывает запрос и устраняет оба подзапроса.
Повышение производительности
Вы не указали, к каким проблемам вы пытались обратиться, исключив LEFT OUTER JOIN
;в любом случае может быть тонкая точка, в которой следует проявлять определенную осторожность.
В принципе, есть две возможности выполнить план
1) сначала присоединиться, а затем объединиться, чтобы получить MAX (wait_position)
2) сначала получить значение MAX, а затем соединить (тривиально) две таблицы из одной строки
Если в таблице ASSINMENT
есть только число строк с заданным значением ITEM_ID
, то практическинет никакой разницы между этими двумя вариантами.
Проблемы начинаются, если вы сталкиваетесь с назначением с тоннами строк для некоторых ITEM_ID
.Это иногда называется умиранием в NESTED LOOPS , поскольку вы зацикливаетесь на миллионах строк только после этого, агрегируя результат в одну строку, содержащую MAX
.
Oracle для этого случая.доступ к индексу INDEX RANGE SCAN (MIN/MAX)
, который получает максимальное значение непосредственно из индекса (без доступа к таблице и без сканирования всех значений - помните, что индекс отсортирован, поэтому получить значение MAX тривиально).
Итак, согласно предположениюиз этих индексов вы можете попробовать альтернативный запрос ниже
уникальный индекс по классам (item_id) индекс по WLF_ASSIGNMENT_RECORDS_F (item_id, wait_position)
Второй индекс важен, так как вы обойдететаблица вообще и вы ее до максимума (wait_position)
Запрос (упрощенный пропуском некоторых столбцов) выглядит следующим образом:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Обратите внимание, что подзапрос вычисляетmax(wait_position)
.На следующем шаге вы outer joins
переводите таблицу в подзапрос, но только если c.enable_capacity = 'Y' - дальнейший предикат не требуется, так как оба источника строк имеют максимум одну строку.
План выполнения очень эффективенсостоящий из доступа к индексам буксировки и доступа к одному блоку таблиц.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
Мне нравится этот пример в качестве демонстрации того, что простой и компактный письменный запрос приводит к эффективному плану выполнения.
Последнее замечание. Я надеюсь, что в вашей производственной среде вы используете переменные связывания, а не буквальные ключи для item_id
;)