UNION с предложением WHERE

Я делаю UNION из двух запросов к базе данных Oracle. Оба они имеют пункт WHERE. Есть ли разница в производительности, если я выполняю WHERE после UNION выполнения запросов по сравнению с выполнением предложения UNION после WHERE?

Например:

SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colA, colB FROM tableB WHERE colA > 1

в сравнении с:

SELECT * 
  FROM (SELECT colA, colB FROM tableA
        UNION
        SELECT colA, colB FROM tableB) 
 WHERE colA > 1

Я полагаю, что во втором случае он выполняет полное сканирование обеих таблиц, влияющих на производительность. Это правильно?


person MNIK    schedule 25.03.2011    source источник
comment
Получите план объяснения и докажите свою веру. А после этого запустите тест и засеките время в своей среде и посмотрите, какой из них победит.   -  person Kuberchaun    schedule 25.03.2011
comment
Для такого простого запроса, скорее всего, нет никакой разницы, потому что Oracle, скорее всего, вставит предикат (предложение WHERE) в производную таблицу/встроенное представление.   -  person OMG Ponies    schedule 26.03.2011
comment
Может быть полезно знать, какую версию Oracle вы используете.   -  person EvilTeach    schedule 28.03.2011


Ответы (8)


По моему опыту, Oracle очень хорошо продвигает простые предикаты. Следующий тест был сделан на Oracle 11.2. Я совершенно уверен, что он создает один и тот же план выполнения для всех выпусков 10g.

(Пожалуйста, не стесняйтесь оставлять комментарии, если вы запускали более раннюю версию и пробовали следующее)

create table table1(a number, b number);
create table table2(a number, b number);

explain plan for
select *
  from (select a,b from table1
        union 
        select a,b from table2
       )
 where a > 1;

select * 
  from table(dbms_xplan.display(format=>'basic +predicate'));

PLAN_TABLE_OUTPUT
---------------------------------------
| Id  | Operation            | Name   |
---------------------------------------
|   0 | SELECT STATEMENT     |        |
|   1 |  VIEW                |        |
|   2 |   SORT UNIQUE        |        |
|   3 |    UNION-ALL         |        |
|*  4 |     TABLE ACCESS FULL| TABLE1 |
|*  5 |     TABLE ACCESS FULL| TABLE2 |
---------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------    
   4 - filter("A">1)
   5 - filter("A">1)

Как вы можете видеть на шагах (4,5), предикат опускается и применяется перед сортировкой (объединением).

Я не мог заставить оптимизатор вытолкнуть весь подзапрос, такой как

 where a = (select max(a) from empty_table)

или присоединение. При наличии правильных ограничений PK/FK это может быть возможно, но очевидно, что ограничения есть :)

person Ronnis    schedule 26.03.2011

Просто предостережение

Если вы попытались

SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB WHERE colA > 1

в сравнении с:

SELECT * 
  FROM (SELECT colA, colB FROM tableA
        UNION
        SELECT colX, colA FROM tableB) 
 WHERE colA > 1

Затем во втором запросе colA в предложении where на самом деле будет иметь colX из tableB, что делает его совершенно другим запросом. Если столбцы имеют псевдонимы таким образом, это может привести к путанице.

person Gary Myers    schedule 27.03.2011

ПРИМЕЧАНИЕ. Несмотря на то, что мой совет был верным много лет назад, оптимизатор Oracle улучшился, так что расположение здесь больше не имеет значения. Однако предпочтение UNION ALL вместо UNION всегда будет правильным, и переносимый SQL должен избегать зависимости от оптимизаций, которые могут быть не во всех базах данных.

Короткий ответ: вам нужно WHERE перед UNION, и вы хотите использовать UNION ALL, если это вообще возможно. Если вы используете UNION ALL, проверьте вывод EXPLAIN, Oracle может быть достаточно умным, чтобы оптимизировать условие WHERE, если оно осталось после него.

Причина в следующем. В определении UNION говорится, что если в двух наборах данных есть дубликаты, их необходимо удалить. Поэтому в этой операции есть неявный GROUP BY, который имеет тенденцию быть медленным. Что еще хуже, оптимизатор Oracle (по крайней мере, 3 года назад, и я не думаю, что он изменился) не пытается проталкивать условия через GROUP BY (неявные или явные). Поэтому Oracle приходится строить большие наборы данных, чем необходимо, группировать их и только потом приступать к фильтрации. Таким образом, предварительная фильтрация везде, где это возможно, официально является хорошей идеей. (Вот почему, кстати, важно по возможности помещать условия в WHERE, а не оставлять их в предложении HAVING.)

Кроме того, если вы знаете, что между двумя наборами данных не будет дубликатов, используйте UNION ALL. Это похоже на UNION тем, что он объединяет наборы данных, но не пытается дедуплицировать данные. Это экономит дорогостоящую операцию группировки. По моему опыту, довольно часто можно воспользоваться этой операцией.

Поскольку в UNION ALL нет неявного GROUP BY, возможно, оптимизатор Oracle знает, как протолкнуть через него условия. У меня нет Oracle для тестирования, поэтому вам нужно будет проверить это самостоятельно.

person btilly    schedule 25.03.2011
comment
Это неверно, по крайней мере, для баз данных, созданных за последние 10 лет. - person Jon Heller; 07.06.2016
comment
@JonHeller В 2016 году вы отвечали на сообщение, написанное в 2011 году, которое было четко помечено как основанное на моем опыте за несколько лет до этого. Многое могло бы измениться, и краткая констатация того, что гораздо полезнее одеяла, — это неправильно. - person btilly; 20.03.2017
comment
Пример в 11.2 Руководство по настройке производительности показывает этот сценарий и как он работает даже с WHERE после UNION. Я не вижу этого в руководстве 10g, так что либо это было новым в 11g, либо не задокументировано в 10g. Может быть несправедливо судить об ответах на основе новой информации, но ответ больше не полезен людям. Я знаю, что незаслуженное отрицание — отстой, но это не хуже, чем давать вводящие в заблуждение советы десяткам тысяч людей. - person Jon Heller; 20.03.2017
comment
@JonHeller Я точно знаю, что его не было в 8, и я почти уверен, что его не было в 10. Однако, если вы собираетесь использовать разные базы данных, на это поведение нельзя полагаться. И предпочтение UNION ALL вместо UNION всегда будет верным на основе стандарта SQL. Но я обновлю ответ. - person btilly; 21.03.2017

Вам нужно посмотреть на планы объяснения, но если в COL_A нет ИНДЕКСА или РАЗДЕЛА, вы просматриваете ПОЛНОЕ СКАНИРОВАНИЕ ТАБЛИЦЫ в обеих таблицах.

Имея это в виду, ваш первый пример выбрасывает некоторые данные, поскольку он выполняет ПОЛНОЕ СКАНИРОВАНИЕ ТАБЛИЦЫ. Этот результат сортируется с помощью UNION, а затем повторяющиеся данные удаляются. Это дает вам набор результатов.

Во втором примере вы извлекаете полное содержимое обеих таблиц. Этот результат, вероятно, будет больше. Таким образом, UNION сортирует больше данных, а затем удаляет дубликаты. Затем фильтр применяется, чтобы дать вам набор результатов, который вам нужен.

Как правило, чем раньше вы отфильтруете данные, тем меньше будет набор данных и тем быстрее вы получите результаты. Как всегда, ваш пробег может отличаться.

person EvilTeach    schedule 27.03.2011

Я бы удостоверился, что у вас есть индекс для ColA, а затем запустил их оба и замерил время. Это даст вам лучший ответ.

person rayman86    schedule 25.03.2011
comment
Я не минусовал, но это может быть реакцией на повсеместное добавление индекса для решения любой проблемы с производительностью. - person Jeffrey Kemp; 31.03.2011

я думаю, это будет зависеть от многих вещей - запустите EXPLAIN PLAN для каждого, чтобы увидеть, что выберет ваш оптимизатор. В противном случае, как предлагает @rayman, запустите их оба и засеките время.

person Randy    schedule 25.03.2011

SELECT * FROM (SELECT colA, colB FROM tableA UNION SELECT colA, colB FROM tableB) as tableC WHERE tableC.colA > 1

Если мы используем объединение, которое содержит одно и то же имя поля в 2 таблицах, нам нужно указать имя для подзапроса как tableC (в приведенном выше запросе). Наконец, условие WHERE должно быть WHERE tableC.colA > 1

person Anbarasi Selvaraj    schedule 17.02.2016
comment
Привет, добро пожаловать в Stack Overflow. Попробуйте добавить небольшое объяснение к вашему ответу. Вы можете сделать это, нажав «Изменить» - person Olivier De Meulder; 17.02.2016

person    schedule
comment
Хотя SQL не дает решения исходного вопроса, вы всегда должны предоставлять вспомогательную информацию для ответа. - person Bonez024; 10.05.2019