2012-05-08 1 views
10

UNION и UNION ALL запросы могут превзойти эквивалентные запросы с использованием OR -связанных предикатов при определенных обстоятельствах. Насколько мне известно, это частично связано с тем, что подзапросы UNION могут выполняться параллельно, и поэтому они могут иметь свой собственный «подплан», специфичный для каждой части предиката OR, который, вероятно, намного более оптимален из-за более простых применимых преобразований запросов ,Пусть Oracle преобразует OR-связанные предикаты в операции UNION ALL.

Но написание OR -связанных предикатов обычно гораздо читабельнее и кратким, даже если факторинг подзапроса был применен к решению UNION ALL. Мой вопрос: есть ли способ указать Oracle, что один дорогостоящий предикат OR должен быть преобразован в операцию UNION ALL? Если есть такой метод/метод, при каких обстоятельствах его можно применять (например, какие-либо ограничения должны присутствовать в столбцах, участвующих в предикатах, и т. Д.)? Пример:

CREATE TABLE a AS 
    SELECT 1 x, 2 y FROM DUAL UNION ALL 
    SELECT 2 x, 1 y FROM DUAL; 

-- This query... 
SELECT * FROM a 
WHERE x = 1 OR y = 1 

-- Is sometimes outperformed by this one, for more complex table sources... 
-- Note: in my case, I can safely apply UNION ALL. I know the two predicates to 
-- be mutually exclusive. 
SELECT * FROM a 
WHERE x = 1 
UNION ALL 
SELECT * FROM a 
WHERE y = 1 

Заметь, я знаю о /*+ USE_CONCAT */ намеке:

SELECT /*+ USE_CONCAT */ * FROM a 
WHERE x = 1 OR y = 1 

Но это, кажется, не производить то, что мне не нужно (не вынужденную UNION ALL операции в плане выполнения):

------------------------------------------- 
| Id | Operation   | Name | E-Rows | 
------------------------------------------- 
| 0 | SELECT STATEMENT |  |  | 
|* 1 | TABLE ACCESS FULL| A |  2 | 
------------------------------------------- 

Возможно, есть некоторые ограничения на этот намек? Для этого я имею Oracle 11g2.

+0

Сколько строк (в% от всех строк) будет возвращено условие «x = 1 или y = 1» (в реальной таблице)? Как насчет использования подсказки 'PARALLEL' в запросе? –

+0

@a_horse_with_no_name: На самом деле (реальное) условие имеет форму '(flag_function() = 1 и condition1) или (flag_function() = 0 и condition2)'. Два под-условия взаимно исключают друг друга в зависимости от PL/SQL 'flag_function()'. Я заметил, что Oracle создает гораздо лучший план для этого при использовании 'UNION ALL', а не при использовании' OR'. 'PARALLEL', вероятно, не поможет, так как количество данных в этом случае не так велико, но план сложный ... Кроме того, этот запрос выполняется очень часто в пользовательских сеансах.Я бы не хотел, чтобы слишком много ресурсов с подсказками 'PARALLEL' –

+1

Эти запросы не эквивалентны. Вы должны использовать UNION вместо UNION ALL. Вы получите разные результаты, когда у вас есть строки, где x и y равны 1. – GriffeyDog

ответ

3

Я считаю, что это может иметь какое-то отношение к индексам, существующим на столбцах, которые вы используете в предикате OR.

Я протестировал, используя следующее в 11gR2.

create table scott.test as 
select level l, 
     decode(mod(level,2), 1, 1, 2) x, 
     decode(mod(level,2), 1, 2, 1) y, 
     dbms_random.value(1, 3) z from dual 
connect by level < 1000; 
/

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

Я объяснил следующие запросы в ЖАБА, (EXPLAIN PLAN FOR)

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4         


select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

Так появляется подсказка не работает. Затем я добавил индекс к у столбцов х &:

create index test_x on test (x, y); 

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

Перезапуск запросы в настоящее время:

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    CONCATENATION            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

Оказывается, что после добавления индекса (даже если он не используется) оптимизатор решил использовать подсказку в конце концов!

Возможно, вы могли бы попробовать это?

+0

Большое спасибо за этот анализ! Действительно, нет индексов (реальные столбцы, представленные) 'x' и' y'. Я попробую добавить индекс на 'x'. 'y', однако, не может быть легко проиндексирован в реальном запросе, так как он исходит из' LEFT OUTER JOIN' ... –

+0

Оптимизатор является обманчивым об этих вещах. Вы можете обнаружить, что он также может попытаться преобразовать растровое изображение в rowids с растровым изображением ИЛИ, если вы индексируете каждый столбец независимо. –

+0

Хорошо, я вижу, я постараюсь следить за этими вещами. После некоторого расследования выясняется, что '' КОНКАТЕНЦИЯ' действительно * * применяется к реальному запросу (я допустил ошибку). Но вы все еще ответили на мой упрощенный вопрос, который, кажется, слишком упрощен ... :) –