1

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

我正在寻找一个通用答案来解释优化器如何处理这些答案中的每一个。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
4

4 回答 4

4

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

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

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

于 2021-09-21T16:59:34.447 回答
4

如果您想了解有关 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]

因此,首先您会看到 aTABLE ACCESS FULL已执行(第 3 行),因此您的索引假设不正确。

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

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

所以不用担心多重计算。

于 2021-09-21T17:37:54.147 回答
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)

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

于 2021-09-21T17:18:12.863 回答
1

另一个有趣的测试方法:

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 不是独占的

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

于 2021-09-21T21:46:17.107 回答