在JOIN之前或之后放置WHERE时的性能差异

时间:2016-06-29 19:56:50

标签: sql oracle performance

Oracle中,如果几个大表之间存在连接,那么它们之间的性能差异是什么:

PLAN A:
SELECT A.a, B.b, C.c
FROM A join B on a.id = b.a
       join C on b.id = c.b
WHERE A.ax = '...' and B.bx = '...' and C.cx = '....';

PLAN B:
SELECT TA.a, TB.b, TC.c
FROM (
         SELECT A.a, A.id
         FROM A
         WHERE A.ax = '...'
     ) as TA
     join
     (
         SELECT B.b, B.a, B.id
         FROM B
         WHERE B.bx = '...'
     ) as TB on TA.id = TB.a
     join
     (
         SELECT C.c, C.b, 
         FROM C
         WHERE C.cx = '...'
     ) as TC on TB.id = TC.b;

PLAN A在所有表连接在一起后放置条件,但PLAN B首先生成每个表的子集然后将它们连接在一起,PLAN B可以比PLAN A表现更好吗?

4 个答案:

答案 0 :(得分:1)

您的两个查询都取决于几个因素,例如索引和不索引的内容,索引策略,服务器负载,数据缓存,您编写查询的方式等等。我假设where中的列在回答时索引子句 -

我运行了两种类型的查询,发现了一个非常相似的结果。 (这可能是因为我的表和列的设置方式)。

解释计划A

的计划
SELECT STATEMENT  FIRST_ROWS
Cost: 9  Bytes: 446  Cardinality: 14    

解释 B计划

的计划
SELECT STATEMENT  FIRST_ROWS
Cost: 12  Bytes: 448  Cardinality: 14   

现在您看到两者花了相同的时间但理想情况下,计划A是最佳实践中的考虑因素(从经验来看,我已经看到Plan A查询几乎被使用了到处都是Plan B而不是那么多)

不同的查询在不同情况下的工作方式不同[而Oracle(或任何SQL引擎)会巧妙地选择最适合您的算法。]

编辑 - 我现在检查了更大的数据集

计划A 计划B 都是相同的

SELECT STATEMENT  FIRST_ROWS
    Cost: 35,413  Bytes: 1,888,512  Cardinality: 59,016 

答案 1 :(得分:0)

两个查询都会产生几乎相同的查询计划。

计划A 将花费更多时间执行Index Seeks(如果使用正确的索引),同时花费更少的时间执行表扫描。

计划B 将花费更多时间执行表格扫描,同时执行索引搜索的时间更少(如果使用适当的索引)。

计划A 更加强大和紧凑。

没有一个直接的答案,我上面所说的是粗略的一瞥答案。确定环境差异的最佳方法是测试结果。实际结果会影响表的设计方式,表的大小,先前的查询执行,缓存的内容以及一些不够简单的其他方面。优化器通常非常擅长选择最佳计划。

答案 2 :(得分:0)

首先,计划取决于表格的统计信息see dbms_stat。正确的统计确保正确的查询执行。例如DBMS_STATS.gather_schema_stats(' SCOTT');按架构see example

存储统计信息

同时,还有一些改进计划的选择:

  1. 不要使用JOIN语法,只是" old"风格"其中"。它强制sql 分析器过滤每个表然后加入。提示/ * +领先()* / 确保表格按正确顺序处理:

    SELECT /*+ leading(a b c) */ A.a, B.b, C.c
      FROM A, B, C 
     WHERE A.ax = '...' 
       and a.id = b.a 
       and B.bx = '...'
       and b.id = c.b 
       and c.cx = '....'; 
    
  2. 在" on"之后的每个表上放置过滤条件条件:

    SELECT A.a, B.b, C.c
      FROM A join B on a.id = b.a and B.bx = '...' 
           join C on b.id = c.b and C.cx = '....'
     WHERE A.ax = '...';
    
  3. 如果您的表格非常庞大并且您想过滤它,请缓存结果 然后加入,你可以使用提示/ * + no_merge()* /:

    SELECT /*+ no_merge(TA) no_merge(TB) no_merge(TC) */ TA.a, TB.b, TC.c
      FROM (
      SELECT A.a, A.id
        FROM A
       WHERE A.ax = '...'
    ) as TA
    join
    (
     SELECT B.b, B.a, B.id
     FROM B
     WHERE B.bx = '...'
    ) as TB on TA.id = TB.a
    join
    (
     SELECT C.c, C.b, 
     FROM C
     WHERE C.cx = '...'
    ) as TC on TB.id = TC.b;
    

    但最好是加入2个小表,哈希结果,然后加入第三个表:

    SELECT /*+ no_merge(AB) */ AB.a, AB.b, TC.c
      FROM (
      SELECT /*+ use_hash(a b) */ A.a, A.id a_id, B.b, B.a b_a, B.id b_id
        FROM A, B
       WHERE A.ax = '...'
         AND a.id = b.a
         AND B.bx = '...'
     ) as AB,
     (
     SELECT C.c, C.b, 
       FROM C
      WHERE C.cx = '...'
     ) as TC 
     WHERE AB.b_id = TC.b;
    

答案 3 :(得分:0)

我想在这里提出上述两个查询的执行计划并进行讨论,但是Pirate X解释得很好,所以我不会提到这一点,我完全同意他,但对我来说,我有时会经历连接条件中的where子句具有更好的性能(在时间的情况下)我的意思是可能编写计划A,因为以下可能更好,没有成本可以测试它:

SELECT A.a, B.b, C.c
FROM A join B on a.id = b.a and A.ax = '...'
       join C on b.id = c.b and B.bx = '...' and C.cx = '....';

以这种方式,至少你没有句法上的where子句。