ORACLE表现:(哈希)加入与子选择按顺序排列在巨大的表格中

时间:2015-11-03 10:50:31

标签: oracle performance join hash subquery

我在ORACLE 11g

中遇到了以下(摘要)问题
  • 有两个名为STRONGWEAK的表。
  • WEAK的主键是由STRONG的主键(称为pk)加上一个附加列(称为fk)组成的复合词。
  • 因此,我们有一个通用的1:N关系。
  • 但是,在实际应用程序中,与N条目相关的WEAK STRONG行中始终只有一行具有特殊关系。
  • 这就是为什么通过将1:1列添加到fk也可以实现额外的STRONG关系。
  • 此外,值得注意的是,这两个表格都很大但索引很好。

整体情况如下:

UML

现在,我必须定义一个视图,其中显示STRONG行以及由1:1关系链接的一些其他列。我尝试了两种基本方法:

子选择

SELECT
(SELECT some_data1 FROM weak WHERE weak.pk = strong.pk AND weak.fk = strong.fk) some_data1,
(SELECT some_data2 FROM weak WHERE weak.pk = strong.pk AND weak.fk = strong.fk) some_data2
FROM strong

左外连接

SELECT
weak.some_data1,
weak.some_data2
FROM strong
LEFT OUTER JOIN weak ON weak.pk = strong.pk AND weak.fk = strong.fk

我首先认为"左外连接" -way必须更好,我仍然认为只要没有WHERE/ORDER_BY - 条款就是这样。但是,在现实世界的应用程序中,用户查询对话框输入是动态的 翻译成上述陈述的扩展。通常,用户知道STRONG的主键,导致这样的查询:

SELECT *
FROM the_view
WHERE the_view.pk LIKE '123%' --Or even the exact key
ORDER BY the_view.pk

使用"左外连接" -way,我们遇到了一些非常严重的性能问题,即使这些SELECT中的大多数只返回几行。我认为发生的事情是哈希表不适合 记忆导致太多I/O - 事件。因此,我们回到了Subselects。

现在,我有几个问题:

Q1

Oracle是否必须为每个SELECT计算整个哈希表(使用ORDER_BY)?

Q2

为什么" Subselect" - 更快?在这里,值得注意的是,这些列也可以出现在WHERE - 子句中。

Q3

加入这两个表是否有可能增加选择行的数量?如果是这样的话:我们可以告诉Oracle,从逻辑角度看这种情况永远不会发生吗?

Q4

如果"左外连接" -Way不是一个表现良好的选项:" Subselect" -way似乎有点多余。还有更好的方法吗?

非常感谢!

修改

根据要求,我将添加实际案例的解释计划。但是,这里有一些重要的事情:

  • 在上面的描述中,我试图简化实际问题。在现实世界中,视图要复杂得多。
  • 由于简化而遗漏的一件事是性能问题主要发生在使用STRONG => WEAK - 加入嵌套连接时(见下文)。实际情况如下:
    • ZV是我们目标视图的名称 - 下面的解释计划是指该视图。
    • Z(~3M​​行)加入T(~1M行)
    • T加入CCPP(约1M行)
    • TV是基于T的视图。来想一想......这可能很关键。前端应用程序限制了我们定义这些视图的方式:在ZV中,我们必须加入TV而不是T,我们无法实现T => CCPP -join TV,强制我们将连接TV => CCPP定义为嵌套连接。
  • 我们只在很多用户的生产环境中遇到了性能问题。显然,我们必须摆脱这些问题。因此,它现在无法复制。以下陈述的回复时间完全正常。

执行计划

----------------------------------------------------------------------------    ----------------------------------
| Id  | Operation                        | Name                         |     Rows  | Bytes |TempSpc| Cost (%CPU)|
---------------------------------------------------------------------------- ----------------------------------
|   0 | SELECT STATEMENT                 |                              |     717K|    73M|       | 13340   (2)|
|   1 |  HASH JOIN OUTER                 |                              |     717K|    73M|    66M| 13340   (2)|
|   2 |   VIEW                           |                              |   687K|    59M|       |     5   (0)|
|   3 |    NESTED LOOPS OUTER            |                              |   687K|    94M|       |     5   (0)|
|   4 |     NESTED LOOPS OUTER           |                              |     1 |   118 |       |     4   (0)|
|   5 |      TABLE ACCESS BY INDEX ROWID | Z                            |     1 |   103 |       |     3   (0)|
|   6 |       INDEX UNIQUE SCAN          | SYS_C00245876                |     1 |       |       |     2   (0)|
|   7 |      INDEX UNIQUE SCAN           | SYS_C00245876                |  1798K|    25M|       |     1   (0)|
|   8 |     VIEW PUSHED PREDICATE        | TV                           |   687K|    17M|       |     1   (0)|
|   9 |      NESTED LOOPS OUTER          |                              |     1 |    67 |       |     2   (0)|
|  10 |       TABLE ACCESS BY INDEX ROWID| T                            |     1 |    48 |       |     2   (0)|
|  11 |        INDEX UNIQUE SCAN         | SYS_C00245609                |     1 |       |       |     1   (0)|
|  12 |       INDEX UNIQUE SCAN          | SYS_C00254613                |     1 |    19 |       |     0   (0)|
|  13 |   TABLE ACCESS FULL              | CCPP                         |  5165K|    88M|       |  4105   (3)|
--------------------------------------------------------------------------------------------------------------

1 个答案:

答案 0 :(得分:1)

真实问题是 - 您的查询返回了多少条记录?

仅10条记录或10.000(或10M),您希望快速查看前10行?

对于字母大小写,子查询解决方案确实更好,因为您不需要排序,只需少量查找WEAK表。

对于以前的情况(即两个表中所选行的数量都很小)我希望执行计划如下:

 --------------------------------------------------------------------------------------------
 | Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
 --------------------------------------------------------------------------------------------
 |   0 | SELECT STATEMENT              |            |     4 |   336 |   100   (1)| 00:00:02 |
 |   1 |  SORT ORDER BY                |            |     4 |   336 |   100   (1)| 00:00:02 |
 |   2 |   NESTED LOOPS OUTER          |            |     4 |   336 |    99   (0)| 00:00:02 |
 |   3 |    TABLE ACCESS BY INDEX ROWID| STRONG     |     4 |   168 |    94   (0)| 00:00:02 |
 |*  4 |     INDEX RANGE SCAN          | STRONG_IDX |   997 |       |     4   (0)| 00:00:01 |
 |   5 |    TABLE ACCESS BY INDEX ROWID| WEAK       |     1 |    42 |     2   (0)| 00:00:01 |
 |*  6 |     INDEX UNIQUE SCAN         | WEAK_IDX   |     1 |       |     1   (0)| 00:00:01 |
 --------------------------------------------------------------------------------------------
 Predicate Information (identified by operation id):
 ---------------------------------------------------

   4 - access("STRONG"."PK" LIKE '1234%')
        filter("STRONG"."PK" LIKE '1234%')
    6 - access("WEAK"."PK"(+)="STRONG"."PK" AND "WEAK"."FK"(+)="STRONG"."FK")
        filter("WEAK"."PK"(+) LIKE '1234%')

如果您在一个或另一个表上看到FULL TABLE SCAN - 优化展示可能是谓词 pk LIKE' 123%' 将返回太多记录和索引访问速度会慢一些。 这可能是一个好的或坏的猜测,因此您可能需要检查您的表统计和基数估计。

以下是一些其他信息

<强> Q1

如果Oracle执行 HASH JOIN ,则必须在内存中读取整个数据源(通常是较小的数据源) 在哈希表中。这是整个表或它的一部分,由WHERE / ON子句过滤 (在您的情况下,只有pk LIKE&#39; 123%&#39;)

的记录

<强> Q2

这可能只是一种印象,因为您很快就会看到第一条记录。子查询被执行 仅适用于前几个获取的行。

要知道必须检查(或发布)执行计划的确切答案。

<强> Q3

不,抱歉,加入这两个表永远不会增加选择的行数但是返回确切的行数 如SQL标准中所定义。

您有责任在唯一/主键上定义连接以避免重复。

第四季度

您当然可以在子查询中选择some_data1 ||'#'||some_data2之类的内容,但这是您的责任 判断它是否安全..