【问题标题】:Performance impact of multiple aggregate functions in HAVING clauseHAVING 子句中多个聚合函数的性能影响
【发布时间】:2021-11-15 05:50:28
【问题描述】:

我想知道下面两个查询之间是否有区别。

我正在寻找一个通用答案来解释优化器如何处理这些答案。 t.id 上有一个索引。

Oracle的版本是11g。

select t.id, sum(t.amount)
from transaction t
group by t.id
having sum(t.amount) between -0.009 and 0.009
select t.id, sum(t.amount)
from transaction t
group by t.id
having sum(t.amount) >= -0.009 and sum(t.amount)<= 0.009

【问题讨论】:

    标签: sql oracle oracle11g


    【解决方案1】:

    在聚合查询中,大部分工作都涉及移动数据。聚合有一些开销,但通常很简单。

    而且,SQL 编译器可以决定是否要重用聚合表达式。仅仅因为您在查询中使用了两次sum(amount) 并不意味着它会被执行两次。

    一些聚合函数更昂贵——尤其是在字符串上或使用distinct。您可以随时测试查询以查看是否有很大影响,但一般来说,您应该担心您的逻辑是否正确,而不是您使用聚合函数的次数。

    【讨论】:

      【解决方案2】:

      如果您想了解有关 CBO 决定执行 SQL 语句的步骤的基本信息,请使用explain plan

      例子

      EXPLAIN PLAN  SET STATEMENT_ID = 'jara1' into   plan_table  FOR
      select DEPARTMENT_ID, sum(salary) 
      from HR.employees
      group by DEPARTMENT_ID
      having sum(salary) between 5000 and 10000
      ;
      --    
      SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
      

      查询返回

      Plan hash value: 244580604
       
      ---------------------------------------------------------------------------------
      | Id  | Operation           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
      ---------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT    |           |     1 |     7 |     4  (25)| 00:00:01 |
      |*  1 |  FILTER             |           |       |       |            |          |
      |   2 |   HASH GROUP BY     |           |     1 |     7 |     4  (25)| 00:00:01 |
      |   3 |    TABLE ACCESS FULL| EMPLOYEES |   107 |   749 |     3   (0)| 00:00:01 |
      ---------------------------------------------------------------------------------
       
      Query Block Name / Object Alias (identified by operation id):
      -------------------------------------------------------------
       
         1 - SEL$1
         3 - SEL$1 / EMPLOYEES@SEL$1
       
      Predicate Information (identified by operation id):
      ---------------------------------------------------
       
         1 - filter(SUM("SALARY")>=5000 AND SUM("SALARY")<=10000)
       
      Column Projection Information (identified by operation id):
      -----------------------------------------------------------
       
         1 - (rowset=256) "DEPARTMENT_ID"[NUMBER,22], SUM("SALARY")[22]
         2 - (#keys=1; rowset=256) "DEPARTMENT_ID"[NUMBER,22], 
             SUM("SALARY")[22]
         3 - (rowset=256) "SALARY"[NUMBER,22], "DEPARTMENT_ID"[NUMBER,22]
      

      首先你看到TABLE ACCESS FULL被执行(第3行),所以你的索引假设不正确。

      正如在其他答案中指出的那样,您会看到 between 被翻译成与 and 连接的两个谓词(过滤器第 1 行)。

      但对你的问题最重要的是Column Projection,您会看到sum(SALARY) 在第 2 行(HASH GROUP BY 操作)中计算并传递到第 1 行(FILTER),在这两种情况下都只有一次(一个长度为 22 的列)。

      所以不用担心多重计算。

      【讨论】:

      • 完全正确的答案。旁注.. explain plan / dbms_xplan.display 显示了 Oracle 相信它将做什么,但需要一些捷径来确定这一点。运行原始查询(而不仅仅是使用解释计划)并使用 dbms_xplan.display_cursor 将给出 Oracle 实际上所做的。此评论中没有任何内容是错误的,但我会尽可能引导人们使用 dbms_xplan.display_cursor :)
      • 有效备注@Craig - 这将是SELECT t.* FROM table(DBMS_XPLAN.DISPLAY_CURSOR('3brfwdk5vwxkg',null,'ALL')) t; 参数sql_id 可以在gv$sql 中找到基于SQL_FULLTEXT。谢谢。
      【解决方案3】:

      这两个查询绝对没有区别。 between 只是语法糖;解析器立即将between 条件转换为两个不等式,并结合and 运算符。这甚至在优化器看到查询之前就完成了。 (请注意,在这种情况下,解析和优化阶段之间的区别是有意义的,尽管程序员经常将它们视为一个步骤。)

      简单的例子:

      SQL> set autotrace traceonly explain
      SQL> select deptno, sum(sal) as sum_sal
        2  from   scott.emp
        3  group  by deptno
        4  having sum(sal) between 10000 and 20000
        5  ;
      
      Execution Plan
      ----------------------------------------------------------
      Plan hash value: 2138686577
      
      ----------------------------------------------------------------------------
      | Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
      ----------------------------------------------------------------------------
      |   0 | SELECT STATEMENT    |      |     1 |     7 |     4  (25)| 00:00:01 |
      |*  1 |  FILTER             |      |       |       |            |          |
      |   2 |   HASH GROUP BY     |      |     1 |     7 |     4  (25)| 00:00:01 |
      |   3 |    TABLE ACCESS FULL| EMP  |    14 |    98 |     3   (0)| 00:00:01 |
      ----------------------------------------------------------------------------
      
      Predicate Information (identified by operation id):
      ---------------------------------------------------
      
         1 - filter(SUM("SAL")>=10000 AND SUM("SAL")<=20000)
      

      您提到的“索引...”与问题无关。

      【讨论】:

        【解决方案4】:

        另一种有趣的测试方法:

        with function expand_sql_text(text_in varchar2) 
                 return varchar2
             as
                 text_out long;
             begin
                 dbms_utility.expand_sql_text(text_in, text_out);
                 return text_out;
             end expand_sql_text;
        select expand_sql_text(
                 'select * from dual where 2 between 1 and 3'
               ) as text_out
        from   dual
        /
        
        TEXT_OUT
        ------------------------------------------------------------------------------------------------------------------------------------------------------------
        SELECT "A1"."DUMMY" "DUMMY" FROM "SYS"."DUAL" "A1" WHERE 2>=1 AND 2<=3
        
        1 row selected.
        

        在你原来的问题中,第二个谓词是

        having sum(t.amount) > -0.009 and sum(t.amount)< 0.009
        

        这和between的版本不一样,因为between is not exclusive

        通常在 SQL 中,针对简单文字的过滤谓词通常不会导致任何显着的性能开销。在group by 子句中,在聚合之后应用谓词这一事实进一步减少了任何开销。

        【讨论】:

          猜你喜欢
          • 2013-01-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多