Oracle UNION ALL查询占用临时空间

时间:2019-01-03 10:54:03

标签: oracle amazon-redshift

我有这样的查询:

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表(数十亿行),然后将输出存储在temp表空间中。然后它将TEST3和TEST4连接起来,然后将输出保存在相同的临时表中。最后从那里选择记录以显示结果。

我在Redshift和Oracle中都看到了这种行为。我只是想知道为什么从第一个SELECT获得结果后为什么将结果存储在临时段中。是时候占用和占用临时空间了。它不能只是在第一个SELECT完成后才开始显示结果,然后进入第二个(而不是存储)。

2 个答案:

答案 0 :(得分:0)

这个答案有些投机,因为我没有Oracle文档参考。通过检查,我们可以想象您想运行以下查询:

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

很明显,要应用诸如ORDER BY之类的任何设置操作,从联合查询返回的所有记录都必须放在一个逻辑位置。临时表似乎可以工作。

您不是在使用ORDER BY 似乎不会影响Oracle正在使用的工作流程。

我还可以添加Oracle坚持使用临时表的另一个原因。假设有可能将联合的两个半部分直接写入缓冲区。但是,如果以后总联合查询的大小突然超过缓冲区可以容纳的大小,会发生什么情况呢?答案是您的数据库将崩溃。因此,使用临时表是一个安全的选择,通常应该总是有效。

答案 1 :(得分:0)

您如何观察这种行为?您有没有机会执行INSERTCREATE TABLE?那将解释您的观察,因为最后,所有行都是必需的。

此外,如果您的客户设置了选项fetch all rows,则可以观察到这一点。

但是在通常情况下,当客户对前几行不感兴趣 Oracle快速返回第一个联接中的第一个可用行(数组大小)时,会忽略第二个

您可以执行这个小小的Gedanken实验

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;

这将在大约30秒内返回我在SQL Developer中的第一页,从而以某种方式驳斥了您的主张。

简单地计算出两个10 ** 6 * 10 ** 6笛卡尔式行长为1K的所需TEMP空间-这远高于我的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 |       |       |          |
--------------------------------------------------------------------------------------------------------------------------------------

您看到的是Oracle

1)完全扫描了表格2(第5行)

2)从表1中获得一行(第3行)

3)返回第50行(第0行)

4)表3和4未分配(第7和9行)

您可以简单地将示例适应于内部联接,以查看相似的结果。