【问题标题】:INNER JOIN performance with '<' or '>' condition'<' 或 '>' 条件的 INNER JOIN 性能
【发布时间】:2017-05-07 05:52:54
【问题描述】:

我有两个表,列 SessionOrder。此列是整数数据类型,具有以下索引:CREATE INDEX OSIDX_&lt;internal name&gt; ON &lt;Entity&gt;

我正在执行以下查询:

SELECT i_0.rn, i_1.rn 
FROM (
    SELECT "RawEvent"."SessionOrder" as rn
    FROM "RawEvent" i_0
    WHERE something = 12
)
INNER JOIN (
    SELECT "RawEvent"."SessionOrder" as rn
    FROM "RawEvent" i_1
    WHERE something = 14
) ON i_0.rn > i_1.rn

这个查询的问题是ON i_0.rn &gt; i_1.rn 变得太慢并且超时。 我将其替换为ON i_0.rn = i_1.rn,它非常快,但显然不会产生预期的结果。

有人知道一种方法来提高此查询的性能避免超时吗? 这个问题的另一个目标是了解为什么 ON i_0.rn &gt; i_1.rn 会表现不佳。

PS:无法增加超时时间

【问题讨论】:

  • 执行计划表明它在做什么?每个子查询找到多少行? (为什么你使用子查询而不是仅仅加入表?这看起来不像 Oracle 语法)。
  • 这不是一个有效的查询。表的别名没有定义在ON i_0.rn &gt; i_1.rn的范围内
  • 我同意 Alex 的观点,这绝对不是 Oracle 语法。也就是说,这几乎是一个笛卡尔连接。您真的打算将小于 i_o.rn 的每一行加入 i_o 吗?例如,此 SQL 返回 4,950 行:with aset as (select rownum r from dba_objects where rownum b.r.
  • 这种连接总是很昂贵。如果这些表非常大,那么结果可能会太大以至于不值得尝试进行这项工作。例如,如果两个子查询仅返回 1000 行且具有相同的 SessionOrder 集,那么您的结果集将是 499,500 行。我建议寻找一种更好的方法来解决根本问题。
  • 我强制从Oracle标签切换到SQL-Server,如果你确定它是oracle请改回来(但是这个语法根本不像oracle...)

标签: sql oracle indexing inner-join


【解决方案1】:

您的查询执行三个任务:

1) 获取两个子集(12 和 14)的数据

2) 加入数据并

3) 将结果传递给客户端

请注意,索引访问(您怀疑会导致问题)仅与第 1 步相关。 所以要想得到更好的印象,首先要实现三个步骤之间经过的时间分布。 这可以使用 SQL*Plus 来完成(我使用的生成数据与我之前的答案相同)

数据访问

由于我的表没有索引,因此执行 count(*) 会执行 FULL TABLE SCAN。因此,在最坏的情况下,使用两倍的时间来获取数据。

SQL> set timi on
SQL> set autotrace on
SQL> select count(*) from mytab;

  COUNT(*)
----------
     20000

Elapsed: 00:00:01.13

Execution Plan
----------------------------------------------------------
Plan hash value: 3284627250

--------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |  5472   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE    |       |     1 |            |          |
|   2 |   TABLE ACCESS FULL| MYTAB | 20000 |  5472   (1)| 00:00:01 |
--------------------------------------------------------------------

FTS 在大约一秒钟内就准备好了,所以要让两个组都差不多。两秒钟过去了。

加入

可以通过连接查询的 CTAS 模拟连接所用的时间。

SQL> create table myRes as
  2  select a.SessionOrder rn0, b.SessionOrder rn1
  3  from myTab a join myTab b on a.SessionOrder > b.SessionOrder and
  4  a.something = 12 and b.something = 14;

Table created.

Elapsed: 00:00:23.65

Join 返回近 50M 行(由于大于条件),大约需要 21 秒(我为数据访问减去 2 秒)。

将数据传递给客户

我们使用选项set autotrace traceonly来抑制客户端屏幕上查询的输出,但是数据被传输了,所以我们可以 测量时间。 (如果把结果渲染到屏幕上,时间会长很多)

SQL> SET ARRAYSIZE 5000
SQL> set autotrace traceonly
SQL> select a.SessionOrder rn0, b.SessionOrder rn1
  2  from myTab a join myTab b on a.SessionOrder > b.SessionOrder and
  3  a.something = 12 and b.something = 14;

49995000 rows selected.

Elapsed: 00:03:03.89

Execution Plan
----------------------------------------------------------
Plan hash value: 2857240533

-----------------------------------------------------------------------------
| Id  | Operation           | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |       |    49M|   667M| 11077   (2)| 00:00:01 |
|   1 |  MERGE JOIN         |       |    49M|   667M| 11077   (2)| 00:00:01 |
|   2 |   SORT JOIN         |       | 10000 | 70000 |  5473   (1)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL| MYTAB | 10000 | 70000 |  5472   (1)| 00:00:01 |
|*  4 |   SORT JOIN         |       | 10000 | 70000 |  5473   (1)| 00:00:01 |
|*  5 |    TABLE ACCESS FULL| MYTAB | 10000 | 70000 |  5472   (1)| 00:00:01 |
-----------------------------------------------------------------------------

这里花费最多的时间大约是 2:40 分钟

总结

因此,在总共 3 分钟以上的场景中,只有大约 2 秒用于数据访问(或大约 1%)。 即使您将数据访问减少到十分之一 - 您也几乎看不出有什么不同。 问题在于连接,甚至更多在于将数据传输到客户端。

何时索引可以提供帮助

当然这取决于...

在一个非常特殊的情况下,您有一个非常大的表,其中包含很少的数据 something in (12,14) 您可以从 AND SessionOrder 上定义的索引中获利。这允许使用 index only 访问完全绕过表访问的数据。

【讨论】:

    【解决方案2】:

    按照建议,我尝试使用其他获得更高性能的策略来解决问题。

    尽管有这个简单的解决方案,但我不明白为什么原来的查询太慢了。我认为Oracle引擎没有使用索引。

    SELECT i_0."SessionOrder",  i_1."SessionOrder"
    FROM "RawEvent" i_0
    INNER JOIN "RawEvent" i_1 ON  i_0."SessionOrder" < i_1."SessionOrder" 
    WHERE i_0."something" = 12 AND i_1."something" = 14
    

    【讨论】:

    • 该查询返回的结果与原始查询不同 - 您必须将 OR 替换为 AND 才能获得相同的结果。此外,如果没有有关您的输入数据的其他信息(行数、某些内容的分布/SessionOrder),就无法判断优化器是否使用索引会产生任何影响。如果您的数据严重倾斜,它可能会决定全表扫描更合适。
    【解决方案3】:

    请先检查您是否真的使用Oracle数据库。您的 SQL 语法建议使用其他 RDBMS 或某些前处理器。

    为了让您对此类查询有什么期望,您可以使用如下的虚拟示例。

    生成样本数据

    create table myTab as 
    with mySeq as 
    (select rownum SessionOrder from dual connect by level <= 10000)
    select 12 something, SessionOrder from mySeq union all
    select 14  something, SessionOrder from mySeq
    ;
    

    这会产生两个子源,每个子源都有 10.000 个从 1 到 10.000 的序列。

    测试查询

    create table myRes as
    select a.SessionOrder rn0, b.SessionOrder rn1
    from myTab a join myTab b on a.SessionOrder > b.SessionOrder and
    a.something = 12 and b.something = 14;
    

    在不到 30 秒的时间内生成 49.995.000 行。

    如果您希望在更短的时间内获得如此大的结果,则需要进行高级优化。在不了解您的数据和要求的情况下,无法提供一般建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-01
      • 2012-06-06
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多