【问题标题】:Oracle UNION ALL query takes temp spaceOracle UNION ALL 查询占用临时空间
【发布时间】:2019-01-03 21:35:36
【问题描述】:

我有一个这样的查询:

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 获得结果后将结果存储在临时段中。它既需要时间,也需要占用临时空间。不能在第一个 SELECT 完成后开始显示结果,然后再进行第二个(而不是存储)。

【问题讨论】:

  • 联合中的两个查询中的每一个都生成一个结果集,并且该数据必须去某个地方。你希望它去哪里?
  • 当我们执行“SELECT * FROM T”时。它只是从磁盘中分块数据,将其发送到缓冲区,然后发送到客户端。为什么在 UNION ALL 的情况下,它从两个 SELECT(存储在内存/磁盘中)收集数据,然后开始分块数据,为什么不是一个为什么一个。
  • 您的查询语法无效 - 您是指CROSS JOIN 还是JOIN,而您省略了ON 谓词?
  • 更正了语法。
  • 如果您退后一步,考虑一下您要实际实现的目标,也许有更好的方法。如果这适用于您,请随时相应地更新您的问题

标签: oracle amazon-redshift


【解决方案1】:

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

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

应该清楚的是,要应用像ORDER BY 这样的任何集合操作,从联合查询返回的所有记录都需要位于一个逻辑位置。临时表似乎可以工作。

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

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

【讨论】:

  • 是的,你提到的例子,存储结果是有效的。但是如果它们只是平面两个或多个 SELECT 与 UNION ALL,为什么它存储结果。我问这个是因为我的查询是一个数据仓库查询,它处理 TB 的表,因此占用了大量的临时空间,
  • 我已经扩展了我的答案,也支持了你的问题。这是我能做的。我没有解决方法,但即使我这样做了,它也可能在某个时候失败。
  • 我同意。有很多问题需要回答,例如 1) 在串行执行中,第一次选择数据被推送到客户端并且第二次查询需要时间来产生结果后会发生什么。客户在得到一半结果后现在正在等待。 2)在并行执行中,数据将如何推送到客户端。它是所有 SELECT 的单个缓冲区,还是它们独立地将数据推送到客户端。尽管无论如何,对于 TB 的表连接和简单的 UNION ALL 查询,临时空间问题仍然相同。谢谢。
【解决方案2】:

你是如何观察到这种行为的?你不执行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 笛卡尔连接所需的 TEMP 空间,行长度为 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) 完全扫描 table2(第 5 行)

2) 从 table1 中获取一行(第 3 行)

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

4) 表 3 和 4 未附加(第 7 行和第 9 行)

您可以简单地将示例调整为您的内部连接以查看类似的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多