Запрос Oracle UNION ALL занимает временное пространство - PullRequest
0 голосов
/ 03 января 2019

У меня такой запрос:

SELECT * FROM TEST1 LEFT OUTER JOIN TEST2 on TEST1.ID=TEST2.ID
UNION ALL
SELECT * FROM TEST3 LEFT OUTER JOIN TEST4 on TEST3.ID=TEST4.ID;

Я вижу здесь следующее поведение: сначала он соединяет таблицы TEST1 и TEST2 (миллиарды строк), а затем сохраняет выходные данные во временном табличном пространстве.Затем он соединяет TEST3 и TEST4, а затем сохраняет результаты в той же временной таблице.И, наконец, выберите записи для отображения результата.

Такое поведение я наблюдаю как в Redshift, так и в Oracle.Мне просто интересно, почему он сохраняет результат во временных сегментах после получения результата из первого SELECT.Пришло время взять и съесть временное пространство.Может ли он просто начать отображать результат после того, как 1-й SELECT завершится, а затем перейдет к 2-му (вместо сохранения).

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Как вы наблюдаете это поведение? Вы случайно не выполняете INSERT или CREATE TABLE? Это объяснило бы ваше наблюдение, потому что в конце все строки обязательны.

Также, если ваш клиент установил опцию fetch all rows, это можно наблюдать.

Но в обычном случае, когда клиент интересуется несколькими первыми строками Oracle быстро возвращает первые доступные (размер массива) строки из первого соединения , игнорируя второе.

Вы можете выполнить этот маленький Геданкенский эксперимент :

create table test1 as 
select rownum id,
lpad('x',1023,'X') pad
from dual connect by level <= 1000000;

Создать аналог таблицы с 2 по 4.

Теперь запустите ваш запрос (адаптированный к правильному синтаксису)

SELECT * FROM TEST1 CROSS  JOIN TEST2
UNION ALL
SELECT * FROM TEST3 CROSS  JOIN TEST4;

Это возвращает для моей первой страницы в SQL Developer примерно через 30 секунд, что как-то опровергает вашу заявку.

Простое вычисление необходимого пространства TEMP для двух 10 ** 6 * 10 ** 6 декартовых соединений со строкой длиной 1K - это намного выше моей конфигурации TEMP.

Один из возможных способов узнать, что на самом деле делает Oracle , - выполнить запрос с подсказкой /*+ gather_plan_statistics */.

Чем получить SQL_ID оператора и проверить фактические строки A-Rows в плане

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


SQL_ID  a9y62gxagups6, child number 0
-------------------------------------
SELECT /*+ gather_plan_statistics */ * FROM TEST1 CROSS  JOIN TEST2 
UNION ALL SELECT * FROM TEST3 CROSS  JOIN TEST4

Plan hash value: 1763392637

--------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  | Writes |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |       |      1 |        |     50 |00:00:28.52 |     166K|    166K|    142K|       |       |          |
|   1 |  UNION-ALL            |       |      1 |        |     50 |00:00:28.52 |     166K|    166K|    142K|       |       |          |
|   2 |   MERGE JOIN CARTESIAN|       |      1 |   1000G|     50 |00:00:28.52 |     166K|    166K|    142K|       |       |          |
|   3 |    TABLE ACCESS FULL  | TEST1 |      1 |   1000K|      1 |00:00:00.02 |       4 |     28 |      0 |       |       |          |
|   4 |    BUFFER SORT        |       |      1 |   1000K|     50 |00:00:28.49 |     166K|    166K|    142K|  1255M|    11M|   97M (0)|
|   5 |     TABLE ACCESS FULL | TEST2 |      1 |   1000K|   1000K|00:00:03.66 |     166K|    166K|      0 |       |       |          |
|   6 |   MERGE JOIN CARTESIAN|       |      0 |   1000G|      0 |00:00:00.01 |       0 |      0 |      0 |       |       |          |
|   7 |    TABLE ACCESS FULL  | TEST3 |      0 |   1000K|      0 |00:00:00.01 |       0 |      0 |      0 |       |       |          |
|   8 |    BUFFER SORT        |       |      0 |   1000K|      0 |00:00:00.01 |       0 |      0 |      0 |  1103M|    10M|          |
|   9 |     TABLE ACCESS FULL | TEST4 |      0 |   1000K|      0 |00:00:00.01 |       0 |      0 |      0 |       |       |          |
--------------------------------------------------------------------------------------------------------------------------------------

Вы видите, что Оракул

1) полное сканирование таблицы 2 (строка 5)

2) получить одну строку из таблицы 1 (строка 3)

3) вернуться к первому разряду 50 рядов (ряд 0)

4) таблицы 3 и 4 не привязаны (строки 7 и 9)

Вы можете просто адаптировать пример к своему внутреннему соединению, чтобы увидеть похожие результаты.

0 голосов
/ 03 января 2019

Этот ответ несколько умозрительный, потому что у меня нет ссылки на документ Oracle. По результатам проверки мы можем представить, что вы хотели выполнить следующий запрос:

SELECT * FROM TEST1 JOIN TEST2
UNION ALL
SELECT * FROM TEST3 JOIN TEST4
ORDER BY some_col;

Должно быть ясно, что для применения любой операции множеств, такой как ORDER BY, все записи, возвращаемые из запроса объединения, должны быть в одном логическом месте. Кажется, что временная таблица работает.

То, что вы не используете ORDER BY, похоже, не влияет на рабочий процесс, который использует Oracle.

Я также могу добавить еще одну причину, по которой Oracle настаивает на использовании временной таблицы здесь. Предположим, что можно было бы записать обе половины объединения непосредственно в буфер. Но что произойдет, если на более позднем этапе размер общего запроса объединения внезапно превысит размер буфера? Ответ в том, что ваша база данных потерпит крах. Таким образом, использование временной таблицы является безопасной ставкой, которая обычно должна работать всегда.

...